#!/usr/bin/perl

use strict;
use lib 'lib';

use Video::DVDRip::Project;

my $USAGE = <<_EOU;
Usage: dvdrip-replex project-file
_EOU

my $PRINT_STDERR = 0;

#-- These variables are initialized from the dvd::rip project file later.
#-- Hey, the whole thing is a hack, so don't complain about some globals... ;)
my $PROJECT      = "foo";
my $SUB_ID       = 0;
my $TITLE_NR     = 1;
my $AUDIO_TRACK  = 0;
my $FPS          = 25;

my $PROJECT_DIR  = "/mega/dvdrip/$PROJECT";
my $VOB_DIR      = sprintf("$PROJECT_DIR/vob/%03d", $TITLE_NR);
my $AVI_DIR      = sprintf("$PROJECT_DIR/avi/%03d", $TITLE_NR);
my $IFO_DIR      = "$PROJECT_DIR/tmp/ifo";
my $NAV_LOG_FILE = sprintf("$PROJECT_DIR/tmp/%s-%03d-nav.log", $PROJECT, $TITLE_NR);
my ($FRAMES)     = 0; # qx[ wc -l $NAV_LOG_FILE ] =~ /(\d+)/;
my $RUNTIME      = int($FRAMES/$FPS);

my $AUDIO_FIFO       = "$AVI_DIR/audio.fifo";
my $VIDEO_FIFO       = "$AVI_DIR/video.fifo";
my $VIDEO_AUDIO_FIFO = "$AVI_DIR/video_audio.fifo";
my $SPU_PALETTE_FILE = "$AVI_DIR/sub-palette.txt";
my $TARGET_VOB_FILE  = "$AVI_DIR/video_audio_spu.fifo";
my $DVD_TARGET_DIR   = "$AVI_DIR/dvd-image";

my $VOB_SIZE = 0;

$| = 1;

main: {
	my $filename = shift @ARGV;
	
	if ( !$filename ) {
		print $USAGE;
		exit 1;
	}
	
	read_project_file($filename);
	
	my $factor = calc_factor();

	replex_video($factor);
	dvdauthor_finish();
}

sub read_project_file {
	my ($file) = @_;
	
	my $project = Video::DVDRip::Project->new_from_file( filename => $file );
	my $title   = $project->content->selected_title;

	$PROJECT      = $project->name;
	$TITLE_NR     = $title->nr;
	$AUDIO_TRACK  = $title->audio_channel;
	$FPS          = $title->frame_rate;

	foreach my $sub ( values %{$title->subtitles} ) {
		if ( $sub->tc_render ) {
			$SUB_ID = $sub->id;
			last;
		}
	}

	if ( ! defined $SUB_ID ) {
		print "Warning: no subtitle activated!\n";
	}

	$VOB_DIR      = $title->vob_dir;
	$AVI_DIR      = $title->avi_dir;
	$IFO_DIR      = $project->ifo_dir;
	$NAV_LOG_FILE = $title->vob_nav_file;
	($FRAMES)     = qx[ wc -l $NAV_LOG_FILE ] =~ /(\d+)/;
	$RUNTIME      = int($FRAMES/$FPS);

	$AUDIO_FIFO	  = "$AVI_DIR/audio.fifo";
	$VIDEO_FIFO	  = "$AVI_DIR/video.fifo";
	$VIDEO_AUDIO_FIFO = "$AVI_DIR/video_audio.fifo";
	$SPU_PALETTE_FILE = "$AVI_DIR/sub-palette.txt";
	$TARGET_VOB_FILE  = "$AVI_DIR/video_audio_spu.fifo";
	$DVD_TARGET_DIR   = "$AVI_DIR/dvd-image";

	1;	
}

sub calc_factor {
	my $tcprobe  = qx[ tcprobe -i $VOB_DIR/ 2>/dev/null];

	my ($ac3_bitrate) = $tcprobe =~ /audio\s+track:\s*-a $AUDIO_TRACK.*?bitrate=(\d+)/s;

	my ($vob_size) = qx[ du -sk $VOB_DIR ] =~ /(\d+)/;
	my ($ac3_size) = int($ac3_bitrate * 1000 / 8 * $RUNTIME / 1024);

	my $all_audio_size;
	while ( $tcprobe =~ /audio\s+track:\s*-a\s*(\d).*?bitrate=(\d+)/sg ) {
		my $audio_size += int($2 * 1000 / 8 * $RUNTIME / 1024);
#		print "audio track $1: $audio_size\n";
		$all_audio_size += $audio_size;
	}

#	print "tcprobe: ".$tcprobe."\n";

	print "runtime         = ${RUNTIME}s\n";
	print "vob_size        = $vob_size\n";
	print "ac3_rate        = $ac3_bitrate\n";
	print "ac3_size        = $ac3_size\n";
	print "all audio size  = $all_audio_size\n";
	
	my $dvd_size = int(4.7*1000000000/1024);

	my $source_vid_size = $vob_size - $all_audio_size;
	my $target_vid_size = $dvd_size - $ac3_size;

	my $factor = sprintf ("%.2f", $source_vid_size / $target_vid_size);

	print "dvd size        = $dvd_size\n";
	print "source vid size = $source_vid_size\n";
	print "target vid size = $target_vid_size\n";
	print "-------------------------\n";
	print "factor          = $factor\n";

	if ( $factor < 1 ) {
		print "\n".
		      "Note: video fits on a DVD-R anyway,\n".
		      "      no requantizing applied.\n\n";
		$factor = 0;
	}

	$VOB_SIZE = int($vob_size/1024);

	return $factor;
}

sub extract_spu {
	if ( -f "$AVI_DIR/sub-$SUB_ID/sub-$SUB_ID.xml" ||
	     ! defined $SUB_ID ) {
		print "SKIPPING extract_spu()\n";
		return;
	}

	system (get_extract_spu_command());
}

sub replex_video {
	my ($factor) = @_;
	
	system ("rm -f  $AUDIO_FIFO $VIDEO_FIFO $VIDEO_AUDIO_FIFO $TARGET_VOB_FILE;
	         mkfifo $AUDIO_FIFO $VIDEO_FIFO $VIDEO_AUDIO_FIFO $TARGET_VOB_FILE");

	my $video_command       = get_video_command($factor);
	my $audio_command       = get_audio_command();
	my $mplex_command       = get_mplex_command();
	my $spumux_command      = get_spumux_command();
	my $dvdauthor_command   = get_dvdauthor_command();
	my $extract_spu_command = get_extract_spu_command();

	if ( $PRINT_STDERR ) {
		s!2>.*/dev/null!! for ($video_command, $audio_command, $mplex_command,
				       $spumux_command, $dvdauthor_command);
	}

	print "ex spu command    = $extract_spu_command\n";
	print "video_command     = $video_command\n";
	print "audio_command     = $audio_command\n";
	print "mplex_command     = $mplex_command\n";
	print "spumux_command    = $spumux_command\n";
	print "dvdauthor command = $dvdauthor_command\n";

	print "\nHit [Enter] or [Ctrl+C]: ";
	<STDIN>;

	print "\n";

	extract_spu();

	print "start dvdauthor command\n";
	system ("$dvdauthor_command &");
	
	print "start spumux command\n";
	system ("$spumux_command &");
	print "start mplex command\n";
	system ("$mplex_command &");
	print "start audio command\n";
	system ("$audio_command &");

	print "start video command\n\n";
	open (VIDEO, "($video_command) 2>&1 |") or die "can't fork $video_command";
	my ($p, $i, $max);
	while (<VIDEO>) {
		($i, $max) = m!(\d+)/(\d+)!;
		$p = sprintf("%.2f\%\r", 100 * $i / $max) if $max;
		print "Progress: $p";
	}
	print "\n";
	close VIDEO;

	system ("rm -f  $AUDIO_FIFO $VIDEO_FIFO $VIDEO_AUDIO_FIFO");

	1;	
}

sub dvdauthor_finish {
	print "\nFinalizing DVD image...\n";
	system(get_dvdauthor_finish_command());
	print "DVD image finalized\n";
}

sub get_extract_spu_command {
	my $nr = $TITLE_NR;
	if ( ! -f sprintf("$IFO_DIR/vts_%02d_0.ifo", $nr) ) {
		$nr = 1;
	}
	return	"mkdir -p $AVI_DIR/sub-$SUB_ID && ".
		"ifo_dump $IFO_DIR $nr | grep Color | sed 's/Color ..: 00//' > $SPU_PALETTE_FILE && ".
		"spuunmux -v 2 -s $SUB_ID -p $SPU_PALETTE_FILE -o $AVI_DIR/sub-$SUB_ID/sub-$SUB_ID $VOB_DIR/*.vob";
}

sub get_video_command {
	my ($factor) = @_;
	return "cat $VOB_DIR/*.vob | dr_progress -m $VOB_SIZE -i 1 | tcextract -x mpeg2 -t vob 2>/dev/null >$VIDEO_FIFO"
		if $factor < 1;
	return "cat $VOB_DIR/*.vob | dr_progress -m $VOB_SIZE -i 1 | tcextract -x mpeg2 -t vob 2>/dev/null | tcrequant -f $factor 2>/dev/null >$VIDEO_FIFO";
}

sub get_audio_command {
	return "cat $VOB_DIR/*.vob | tcextract -a $AUDIO_TRACK -x ac3 -t vob 2>/dev/null >$AUDIO_FIFO";
}

sub get_mplex_command {
	return "mplex -f 8 -o $VIDEO_AUDIO_FIFO $VIDEO_FIFO $AUDIO_FIFO -S 0 >/dev/null 2>&1";
}

sub get_spumux_command {
	return "cat < $VIDEO_AUDIO_FIFO > $TARGET_VOB_FILE 2>/dev/null"
		if ! defined $SUB_ID;
	return "spumux -m dvd -s 0 $AVI_DIR/sub-$SUB_ID/sub-$SUB_ID.xml ".
	       "< $VIDEO_AUDIO_FIFO > $TARGET_VOB_FILE 2>/dev/null";
}

sub get_dvdauthor_command {
	return  "rm -rf $DVD_TARGET_DIR && ".
		"mkdir -p $DVD_TARGET_DIR && ".
		"dvdauthor -t -a ac3+de -o $DVD_TARGET_DIR $TARGET_VOB_FILE 2>/dev/null";
}

sub get_dvdauthor_finish_command {
	return "dvdauthor -T -o $DVD_TARGET_DIR";
}

1;
