#!/usr/bin/perl -W

use CRS::Auphonic;
use CRS::Executor;
use CRS::Tracker::Client;
use CRS::Paths;
use boolean;
use Digest::MD5;


sub getMD5 {
	my $filename = shift;
	my $languages = shift;
	open (my $fh, '<', $filename) or die "Can't open '$filename': $!";
	binmode ($fh);
	my $hash = Digest::MD5->new->addfile($fh)->hexdigest;
	my $languageCount = scalar(keys(%{$languages}));
	return $hash . '-' . $languageCount;
}

sub getLanguages {
	my $props = shift;
	my %languages;
	foreach my $key (keys %{$props}) {
		if ($key =~ /^Record.Language.(\d+)$/) {
			$languages{$1} = $props->{$key};
			print "found language $1 " . $props->{$key} . "\n";
		}
	}
	return %languages;
}

# fetch ticket ready to state postencoding, thus ready to be transmitted to auphonic
my $tracker = CRS::Tracker::Client->new();
my $ticket;
if (defined($ENV{'CRS_ROOM'}) && $ENV{'CRS_ROOM'} ne '') {
        my $filter = {};
        $filter->{'Fahrplan.Room'} = $ENV{'CRS_ROOM'};
        $ticket = $tracker->assignNextUnassignedForState('encoding', 'postencoding', $filter);
} else {
        $ticket = $tracker->assignNextUnassignedForState('encoding', 'postencoding');
}

my $didsomething = 0;

if (!defined($ticket) || ref($ticket) eq 'boolean' || $ticket->{id} <= 0) {
	print "currently no tickets for postencoding\n";
} else {
	my $tid = $ticket->{id};
	my $props = $tracker->getTicketProperties($tid);
	my $paths = CRS::Paths->new($props);
	my $vid = $props->{'Fahrplan.ID'};
	print "got ticket # $tid for event $vid\n";

	my $auphonicflag = 'no';
	$auphonicflag = $props->{'Processing.UseAuphonic'} if defined ($props->{'Processing.UseAuphonic'});
	$auphonicflag = 'no' unless (defined ($props->{'Processing.Auphonic.Enable'}));
	$auphonicflag = $props->{'Processing.Auphonic.Enable'} if (defined ($props->{'Processing.Auphonic.Enable'}) && $auphonicflag eq 'yes');
	my $reuseaudioflag = 'no';
	$reuseaudioflag = $props->{'Processing.Auphonic.ReuseProduction'} if (defined ($props->{'Processing.Auphonic.ReuseProduction'}) && $auphonicflag eq 'yes');

	if ($auphonicflag ne 'yes') {
		my $jobfile = $tracker->getJobFile($tid);
		my $ex = new CRS::Executor($jobfile);
		unless (defined($ex)) {
			$tracker->setTicketFailed($tid, "Postencoding script: instantiating job executor failed!");
			exit;
		}

		my $time = time;
		my $return = 0;

		eval {
			$return = $ex->execute('postencoding');
		};

		if ($return) {
			$time = time - $time;
			$log = join ("\n", $ex->getOutput());
			$tracker->addLog($tid, $log);
			$tracker->setTicketDone($tid, "Postencoding tasks completed in $time seconds");
			# indicate short sleep to wrapper script
			exit(100);
		} else {
			$log = join ("\n", $ex->getErrors());
			$tracker->setTicketFailed($tid, 'postencoding failed:' . $log);
			exit;
		}
	}

	# check config properties for auphonic
	unless (defined ($props->{'Processing.Auphonic.Token'}) and defined ($props->{'Processing.Auphonic.Preset'})) {
			$tracker->setTicketFailed($tid, "postencoding with Auphonic failed: Property Processing.Auphonic.Token or Processing.Auphonic.Preset missing!");
			die;
	}

	# extract languages from properties into separate hash
	my %languages = getLanguages($props);

	# check if there is already a production and if the file changed since then
	my $oldMD5 = $props->{'Processing.Auphonic.SourceFileHash'};
	my $sourcefile = $paths->getPath('Tmp').'/'.$vid.'-'.$props->{'EncodingProfile.Slug'}.".".$props->{'EncodingProfile.Extension'};
	if (! -f $sourcefile) {
		print STDERR "WARNING: no source file found ('$sourcefile'), possibly repeating production uneccessarily\n";
		($oldMD5, $sourcefile) = (undef, undef);
	}

	my $isIdentical = 0;
	my $newMD5 = getMD5($sourcefile, \%languages) if defined($sourcefile);
	if (defined($oldMD5) && defined($newMD5) && $oldMD5 eq $newMD5) {
		$isIdentical = 1;
		print "source file did not change, not repeating productions\n";
	}
	if ($reuseaudioflag eq 'yes') {
		$isIdentical = 1;
		print "reuse audio track requested, skipping authonic upload\n";
	}

	# auphonic authentication via token - the token is stored as a project property in the tracker
	my $auphonicToken = $props->{'Processing.Auphonic.Token'};

	# the chosen auphonic preset configures all filters
	my $auphonicPreset = $props->{'Processing.Auphonic.Preset'};

	# demuxed mp2 in mpeg-ts as generated by the encoding-profile
	my $auphonic = CRS::Auphonic->new($auphonicToken);

	if (defined($props->{'Processing.Auphonic.SpeedLimit'})) {
		$auphonic->setSpeedLimit($props->{'Processing.Auphonic.SpeedLimit'});
	}

	my %props_new = ( );
	$props_new{'Processing.Auphonic.SourceFileHash'} = $newMD5 if defined($newMD5);

	# upload audio-file(s) to auphonic and start the production, if source file changed
	if ($isIdentical == 0) {
		foreach my $langIndex (keys %languages) {
			$langIndex += 1; # currently the filename indexes start with 1 and not 0
			my $audioSrcFile = $paths->getPath('Tmp') .
				$vid.'-'.$props->{'EncodingProfile.Slug'}.'-audio'.$langIndex.'.ts';
			print "Starting production for audio track $langIndex\n";
			my $production = $auphonic->startProduction($auphonicPreset, $audioSrcFile, 
				$props->{'Project.Slug'}.'-'.$vid.'-audio'.$langIndex);
			if (!defined($production)) {
				my $error = $auphonic->getError();
				print STDERR "Starting production for audio track $langIndex failed! Error: $error\n";
				$tracker->setTicketFailed($tid, "Starting production for audio track $langIndex failed! Reason: $error");
				die;
			}
			my $uuid = $production->getUUID();
			print "Started production for audio track $langIndex as '$uuid'\n";
			$props_new{'Processing.Auphonic.ProductionID'.$langIndex} = $uuid;
		}
		# upload changed properties to tracker
		$tracker->setTicketProperties($tid, \%props_new);
		$didsomething = 1;
	} else {
		print "not repeating auphonic production for recording of '$vid'\n";
	}

}

# query tickets that are in postencoding state and assigned to this worker
print "querying for assigned ticket in state postencoding ...\n";
my $tickets = $tracker->getAssignedForState('encoding', 'postencoding');

if (!($tickets) || 0 == scalar(@$tickets)) {
	print "no assigned tickets currently postencoding. exiting...\n";
	exit 0;
}

print "found " . scalar(@$tickets) ." tickets\n";
foreach (@$tickets) {
	my $ticket = $_;
	my $tid = $ticket->{id};
	my $props = $tracker->getTicketProperties($tid);
	my $paths = CRS::Paths->new($props);
	my $vid = $props->{'Fahrplan.ID'};
	print "got ticket # $tid for event $vid\n";

	my $auphonicflag = 'no';
	$auphonicflag = $props->{'Processing.UseAuphonic'} if defined ($props->{'Processing.UseAuphonic'});
	$auphonicflag = 'no' unless (defined ($props->{'Processing.Auphonic.Enable'}));
	$auphonicflag = $props->{'Processing.Auphonic.Enable'} if (defined ($props->{'Processing.Auphonic.Enable'}) && $auphonicflag eq 'yes');

	if ($auphonicflag ne 'yes') {
		# in the case of postencoding without Auphonic this ticket is currently tagged by another worker script. dont touch it.
		next;
	}

	# auphonic login token and uuids of the auphonic productions
	my $auphonicToken = $props->{'Processing.Auphonic.Token'};
	if (!defined($auphonicToken)) {
		$tracker->setTicketFailed($tid, 'Stale postencoding ticket? No Auphonic Token!');
		next;
	}

	# extract languages from properties into separate hash
	my %languages = getLanguages($props);

	my $incomplete = 0;
	foreach my $langIndex (keys %languages) {
		$langIndex += 1; # currently the filename indexes start with 1 and not 0
		my $uuid = $props->{'Processing.Auphonic.ProductionID'.$langIndex};
		if (!defined($uuid)) {
			$tracker->setTicketFailed($tid, 'Stale postencoding ticket? No Auphonic UUID!');
			$incomplete = 1;
			last;
		}
		# poll production states
		my $auphonic = CRS::Auphonic->new($auphonicToken, $uuid);
		if (!$auphonic->isFinished()) {
			print "production $uuid not done yet.. skipping\n";
			$incomplete = 1;
			last;
		}
	}

	if ($incomplete == 1) {
		next;
	}

	# if all audio tracks are complete, proceed with download and remux
	foreach my $langIndex (keys %languages) {
		$langIndex += 1; # currently the filename indexes start with 1 and not 0
		my $uuid = $props->{'Processing.Auphonic.ProductionID'.$langIndex}; # existence is checked in previous loop

		my $dest = $paths->getPath('Tmp').$vid.'-'.$props->{'EncodingProfile.Slug'}.
			'-audio'.$langIndex.'-auphonic.m4a';
		print "downloading audio track $langIndex from Auphonic... to '$dest'\n";
		my $auphonic = CRS::Auphonic->new($auphonicToken, $uuid);
		if (!$auphonic->downloadResult($dest)) {
			$tracker->setTicketFailed($tid, "download of audio track $langIndex from auphonic failed!");
			$incomplete = 1;
			last;
		}
	}

	# some download failed
	if ($incomplete == 1) {
		next;
	}

	# remux via encoding profile job of type "remux"
	print "remuxing audio tracks...\n";
	my $jobfile = $tracker->getJobFile($tid);
	my $ex = new CRS::Executor($jobfile);
	unless (defined($ex)) {
		$tracker->setTicketFailed($tid, "Postencoding script: instantiating job executor failed!");
		exit;
	}

	my $return = 0;

	eval {
		$return = $ex->execute('remux');
	};

	$log = join ("\n", $ex->getOutput());
	$tracker->addLog($tid, $log);
	if ($return) {
		$tracker->setTicketDone($tid, 'Postencoding with Auphonic completed');
		$didsomething = 1;
		print "sleeping a while...\n";
		sleep 5;
	} else {
		$tracker->setTicketFailed($tid, 'Postencoding with Auphonic failed (remuxing step)');
		exit;
	}
}

if ($didsomething == 1) {
	exit(100);
}

