#!/usr/bin/perl -w

###########################################################################
#
# submittranslations.pl
#
# A component of the Greenstone digital library software
# from the New Zealand Digital Library Project at the 
# University of Waikato, New Zealand.
#
# Copyright (C) 1999 New Zealand Digital Library Project
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
###########################################################################


BEGIN {
    die "GSDLHOME not set\n" unless defined $ENV{'GSDLHOME'};
    unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
}


###########################################################################
#
# CODE SYNOPSIS
#
# This script...
#
###########################################################################


use translator;
use util;
use GDBM_File;


my $gsdldir = "$ENV{'GSDLHOME'}";
my $LOG = "STDOUT";


sub main
{
    # Get the code of the source (base) language
    local $sourceabbr = shift(@_);
    # Get the code of the target language
    local $targetabbr = shift(@_);
    # Get the name of the file to translate
    local $transfile = shift(@_);
    # Get the type of media to read from
    local $inputmedia = shift(@_);

    # Read any extra arguments
    local $optionalarg = shift(@_);
    while ($optionalarg) {
	$optionalarg = shift(@_);
    }

    # Log any configuration errors to the general translator log
    local $translatordir = &translator::get_translator_dir();
    local $translatorlogfile = &util::filename_cat($translatordir, "translator.log");
    if (!open(LOG, ">>$translatorlogfile")) {
	die "Error: Could not append to log file $translatorlogfile.\n";
    }

    # Log the command that launched this script
    print $LOG "\nCommand: $0 @ARGV\n";

    # Check that the necessary arguments were supplied
    if (!$sourceabbr || !$targetabbr || !$transfile) {
	print $LOG &get_usage_description();
	print $LOG "Error: Required argument missing.\n";
	die "\n";
    }

    # Check that the languages are specified in the etc/main.cfg file
    local %langhash = &translator::read_languages_from_main_cfg_file();
    if (!$langhash{$sourceabbr}) {
	print $LOG "Error: Language \"$sourceabbr\" is not declared in etc/main.cfg.\n";
	die "\n";
    }
    if (!$langhash{$targetabbr}) {
	print $LOG "Error: Language \"$targetabbr\" is not declared in etc/main.cfg.\n";
	die "\n";
    }

    # Check that the file to translate is defined in the translator config file
    local %files = &translator::read_translation_files_from_config_file();
    if (!$files{$transfile}) {
	print $LOG "Error: File \"$transfile\" is not defined in etc/translator.cfg.\n";
	die "\n";
    }

    # Make sure this is a continuation of a previously initialised translation process
    local $languagedir = &util::filename_cat($translatordir, "$sourceabbr-$targetabbr");
    if (!-e $languagedir) {
	print $LOG "Error: Directory $languagedir does not exist (translation not initialised).\n";
	die "\n";
    }

    # Make sure a translation database exists for this file
    local $translationdir = &util::filename_cat($languagedir, $transfile);
    local $translationdb = &util::filename_cat($translationdir, "translation.db");
    if (!-e $translationdb) {
	print $LOG "Error: No translation database exists for $transfile.\n";
	die "\n";
    }

    # Read the details of the file to translate from the translator config file
    local ($sourcefile, $targetfile, $filetype) =
	&translator::read_file_data_from_config_file($sourceabbr, $targetabbr, $transfile);

    # Log all the rest of the output to the log for this translation process
    close(LOG);
    local $translationlogfile = &util::filename_cat($translationdir, "translation.log");
    if (!open(LOG, ">>$translationlogfile")) {
	die "Error: Could not append to log file $translationlogfile.\n";
    }

    # Log the command that launched this script
    print $LOG "\nCommand: $0 @ARGV\n";


    # -----------------------------------------
    #   Refresh the translation database
    # -----------------------------------------

    &translator::refresh_translation($translationdir, $sourcefile, $filetype);


    # -----------------------------------------
    #   Parse the translation submissions
    # -----------------------------------------

    # Parse the submission file
    local %submissions = ();
    if ($inputmedia eq "html") {
	%submissions = &parse_html_submissions($translationdir);
    }
    elsif ($inputmedia eq "excel") {
	%submissions = &parse_excel_submissions($translationdir);
    }
    else {
	print $LOG "Error: Unknown input media \"$inputmedia\".\n";
	die "\n";
    }

    # -----------------------------------------
    #   Validate the translation submissions
    # -----------------------------------------

    # Parse the source language file, building a mapping from chunk key to chunk text
    local $sourcepath = &translator::get_translation_file_path($translationdir, $sourcefile);
    local %sourcehash = &translator::process_file($sourcepath, $filetype);

    # Check that the translations are valid
    foreach $key (keys(%submissions)) {
	local $sourcetext = $sourcehash{$key};

	# Make sure we are still interested in the translation
	if (!defined($sourcetext)) {
	    print $LOG "Warning: Source chunk $key no longer exists (ignoring translation).\n";
	    delete $submissions{$key};
	    next;
	}

	# Make sure the translation is still valid
	if ($sourcetext ne $submissions{$key}[0]) {
	    print $LOG "Warning: Source chunk $key has changed (ignoring translation).\n";
	    print "      Source text: $sourcetext|\n";
	    print "Submission source: $submissions{$key}[0]|\n\n";
	    delete $submissions{$key};
	    next;
	}
    }


    # -----------------------------------------
    #   Update the translation database
    # -----------------------------------------

    # Open the translation database for this file
    local %translationdata = ();
    tie(%translationdata, "GDBM_File", $translationdb, GDBM_WRCREAT, 0666);

    # Add the submissions to the translation database
    foreach $key (keys(%submissions)) {
	local $targettext = $submissions{$key}[1];

	# If a translation has been submitted, write it to the database
	if ($targettext ne "") {
	    print $LOG "Writing translation for chunk with key $key.\n";
	    $translationdata{$key} = $targettext;

	    # If the chunk was marked as needing updating, it now has been
	    if (defined($translationdata{$key . "*"})) {
		delete $translationdata{$key . "*"};
	    }
	}
    }


    # -----------------------------------------
    #   Write the target language file
    # -----------------------------------------

    &write_translated_file($sourceabbr, $sourcepath, $targetabbr, $targetpath,
			   \%translationdata, $filetype);

    # Clean up
    untie(%translationdata);
}


sub get_usage_description
{
    return "Usage: submittranslations.pl <sourceabbr> <targetabbr> <transfile> html|text\n";
}


sub parse_html_submissions
{
    local ($translationdir) = @_;

    # Open the submission file
    local $submissionfilepath = &util::filename_cat($translationdir, "submission.tmp");
    if (!open(SUBMISSIONS, "<$submissionfilepath")) {
	print $LOG "Error: Could not open $submissionfilepath.\n";
	die "\n";
    }
    local @submissions = <SUBMISSIONS>;
    close SUBMISSIONS;

    # Create a hash map containing the submitted translations
    local %translations = ();

    for ($i = 0; $i < scalar(@submissions); $i++) {
	local $line = $submissions[$i];
	$line =~ s/\r$//;  # Remove any nasty carriage returns

	# Does this line contain a submission??
	if ($line =~ /^ \"/ && $line =~ /%3A%3A/) {
	    $line =~ /^ \"(.+)\"=\"(.*)/;
	    local $chunkkey = $1;
	    local $chunktext = $2;

	    # Tidy up the key value
	    # $chunkkey =~ s/%2F/\//g;
	    $chunkkey =~ s/%3A%3A/::/g;

	    # Grab the rest of the text
	    while ($chunktext !~ /\"$/) {
		$i++;
		$line = $submissions[$i];
		$line =~ s/\r$//;  # Remove any nasty carriage returns
		$line =~ s/\n$//;

		$chunktext = $chunktext . "\n" . $line;
	    }
	    $chunktext =~ s/\"$//;

	    if ($chunkkey =~ /^source::(.+)/) {
		local $key = $1;
		$translations{$key}[0] = $chunktext;
	    }
	    if ($chunkkey =~ /^target::(.+)/) {
		local $key = $1;
		$translations{$key}[1] = $chunktext;
	    }
	}
    }

    return %translations;
}


sub parse_excel_submissions
{
    local ($translationdir) = @_;

    # Read the submissions from the submission file
    local $submittedtext = "";
    local $submissionfilepath = &util::filename_cat($translationdir, "submission.txt");
    if (!open(SUBMISSIONS, "<$submissionfilepath")) {
	print $LOG "Error: Could not open $submissionfilepath.\n";
	die "\n";
    }
    while (<SUBMISSIONS>) {
	$submittedtext .= $_;
    }
    close SUBMISSIONS;

    # Create a hash map containing the submitted translations
    local %translations = ();

    $submittedtext =~ s/^(\n)*/\n\n/;
    local @chunks = split(/\n\nsource::/, $submittedtext);
    shift(@chunks);  # Ignore the first (empty) chunk
    foreach $submission (@chunks) {
	local $sourcechunk = (split(/\ntarget::/, $submission))[0];
	local $targetchunk = (split(/\ntarget::/, $submission))[1];

	# Parse the chunk key and chunk text
	$sourcechunk =~ /^(\S+)\s+((.|\n)*)$/;
	local $sourcekey = $1;
	local $sourcetext = $2;
	$targetchunk =~ /^(\S+)\s+((.|\n)*)$/;
	local $targetkey = $1;
	local $targettext = $2;

	# Remove the quotes around multiline chunks
	if ($sourcetext =~ /^\"/ && $sourcetext =~ /\"$/) {
	    $sourcetext =~ s/^\"//;
	    $sourcetext =~ s/\"$//;
	}
	if ($targettext =~ /^\"/ && $targettext =~ /\"$/) {
	    $targettext =~ s/^\"//;
	    $targettext =~ s/\"$//;
	}

        # Remove the blank space Excel adds at the start of each line
	$sourcetext =~ s/\n /\n/g;
	$targettext =~ s/\n /\n/g;

	# Remove Excel's doubled-up quotes
	$sourcetext =~ s/\"\"/\"/g;
	$targettext =~ s/\"\"/\"/g;
	# print "Source text: $sourcetext\n";
	# print "Target text: $targettext\n";

	$translations{$sourcekey}[0] = $sourcetext;
	$translations{$targetkey}[1] = $targettext;
    }

    return %translations;
}


sub write_translated_file
{
    local ($sourceabbr, $sourcepath, $targetabbr, $targetpath, $translationdata,
	   $filetype) = @_;

    if ($filetype =~ /^macrofile/) {
	return &write_translated_macrofile($sourceabbr, $sourcepath,
					   $targetabbr, $targetpath, $translationdata);
    }
    if ($filetype =~ /^resourcebundle/) {
	return &write_translated_resource_bundle($sourceabbr, $sourcepath,
						 $targetabbr, $targetpath, $translationdata);
    }

    print $LOG "Error: Unknown file type $filetype.\n";
    die "\n";
}


sub sort_by_line
{
    return ((split(/-/, $a))[0] <=> (split(/-/, $b))[0]);
}


# ==========================================================================================

sub write_translated_macrofile
{
    local ($sourceabbr, $sourcepath, $targetabbr, $targetpath, $translationdata) = @_;

    # Use the source file to generate a target file that is formatted the same
    if (!open(FILE_IN, "<$sourcepath")) {
  	print $LOG "Error: Could not open file $sourcepath.\n";
  	die "\n";
    }
    local @sourcelines = <FILE_IN>;
    close FILE_IN;

    local %sourcelinehash = &translator::build_key_to_line_mapping($sourcepath, "macrofile");
    local %sourcehash = &translator::build_key_to_text_mapping($sourcepath, "macrofile",
							       %sourcelinehash);

    # Build a mapping from source line to chunk key
    local %linetokeyhash = ();
    foreach $key (keys(%sourcelinehash)) {
	$linetokeyhash{$sourcelinehash{$key}} = $key;
    }

    # Open a file for the translated data
    local $translatedfile = &util::filename_cat($translationdir, "translated.out");
    if (!open(TRANS_FILE, ">$translatedfile")) {
	print $LOG "Error: Could not write $translatedfile.\n";
	die "\n";
    }

    # Write the translated file, modeling it on the source file
    local @lines = keys(%linetokeyhash);
    local $sourceline = 0;
    foreach $key (sort sort_by_line @lines) {
	local $chunkkey = $linetokeyhash{$key};
	local $sourcetext = $sourcehash{$chunkkey};
	local $targettext = $translationdata{$chunkkey};

	local $macrokey = $chunkkey;
	$macrokey =~ s/^([^:]+):://;

	# Fill in the gaps before this chunk starts
	for ($sourceline = $sourceline; $sourceline < (split(/-/, $key))[0]; $sourceline++) {
	    print TRANS_FILE $sourcelines[$sourceline];
	}
	$sourceline = (split(/-/, $key))[1] + 1;

	# If no translation exists for this chunk, show this, and move on
	if ($sourcetext ne "" && $targettext eq "") {
	    print TRANS_FILE "# -- Missing translation: $macrokey\n";
	    next;
	}

	# Grab the source chunk text
	local $chunkstart = (split(/-/, $key))[0];
	local $sourcechunk = $sourcelines[$chunkstart];
	for ($l = ($chunkstart + 1); $l <= (split(/-/, $key))[1]; $l++) {
	    $sourcechunk .= $sourcelines[$l];
	}

	# Is this an icon macro??
	if ($sourcechunk =~ /^\#\# (.*)/) {
	    # Escape any newline characters so the source text is replaced correctly
	    $sourcetext =~ s/\\/\\\\/g;

	    # Build target chunk from the source chunk and the target text
	    my $targetchunk = $sourcechunk;
	    $targetchunk =~ s/$sourcetext/$targettext/;

	    print TRANS_FILE "$targetchunk";
	}

	# No, it is just a normal text macro
	else {
	    print TRANS_FILE "$macrokey [l=$targetabbr] {$targettext}\n";
	}
    }

    close(TRANS_FILE);
}


sub write_translated_resource_bundle
{
    local ($sourceabbr, $sourcepath, $targetabbr, $targetpath, $translationdata) = @_;

    # Use the source file to generate a target file that is formatted the same
    if (!open(FILE_IN, "<$sourcepath")) {
  	print $LOG "Error: Could not open file $sourcepath.\n";
  	die "\n";
    }
    local @sourcelines = <FILE_IN>;
    close FILE_IN;

    local %sourcelinehash = &translator::build_key_to_line_mapping($sourcepath, "resourcebundle");
    local %sourcehash = &translator::build_key_to_text_mapping($sourcepath, "resourcebundle",
							       %sourcelinehash);

    # Build a mapping from source line to chunk key
    local %linetokeyhash = ();
    foreach $key (keys(%sourcelinehash)) {
	$linetokeyhash{$sourcelinehash{$key}} = $key;
    }

    # Open a file for the translated data
    local $translatedfile = &util::filename_cat($translationdir, "translated.out");
    if (!open(TRANS_FILE, ">$translatedfile")) {
	print $LOG "Error: Could not write $translatedfile.\n";
	die "\n";
    }

    # Write the translated file, modelling it on the source file
    local @lines = keys(%linetokeyhash);
    local $sourceline = 0;
    foreach $key (sort sort_by_line @lines) {
	local $chunkkey = $linetokeyhash{$key};
	local $sourcetext = $sourcehash{$chunkkey};
	local $targettext = $translationdata{$chunkkey};

	local $macrokey = $chunkkey;
	$macrokey =~ s/^([^:]+):://;

	# Fill in the gaps before this chunk starts
	for ($sourceline = $sourceline; $sourceline < (split(/-/, $key))[0]; $sourceline++) {
	    print TRANS_FILE $sourcelines[$sourceline];
	}
	$sourceline = (split(/-/, $key))[1] + 1;

	# If no translation exists for this chunk, show this, and move on
	if ($sourcetext ne "" && $targettext eq "") {
	    print TRANS_FILE "# -- Missing translation: $macrokey\n";
	    next;
	}

	print TRANS_FILE "$macrokey:$targettext\n";
    }

    close(TRANS_FILE);
}


&main(@ARGV);
