#!/usr/bin/perl -w

###########################################################################
#
# translator.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 {
    if (!defined($ENV{'GSDLHOME'})) {
	print STDERR "GSDLHOME not set\n";
	return 1;
    }
    unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
}


use util;
use GDBM_File;
use CGI;


my $translationdir;
my $translationdb;
my %translationdata = ();
my $updatedb;
my %updatedata = ();


sub main
{
    # Get the name of the source (base) language
    my $sourcelang = shift(@_);
    # Get the name of the target language
    my $targetlang = shift(@_);

    # Check that both arguments were supplied
    if (!$sourcelang) {
	print STDERR "Error: You didn't supply the name of the source language!\n";
	return 2;
    }
    if (!$targetlang) {
	print STDERR "Error: You didn't supply the name of the target language!\n";
	return 2;
    }

    # Casefold both language names
    $sourcelang =~ tr/A-Z/a-z/;
    $targetlang =~ tr/A-Z/a-z/;

    my $gsdldir = "$ENV{'GSDLHOME'}";
    my $macrosdir = util::filename_cat($gsdldir, "macros");
    my $langdir = util::filename_cat($gsdldir, "tmp", "lang");

    # Make sure this is a continuation of a previously initialised translation process
    $translationdir = util::filename_cat($langdir, "$sourcelang-$targetlang");
    if (! -e $translationdir) {
	print STDERR "Error: Translation has not been initialised (exiting).\n";
	return 3;
    }

    # Parse the source language macro files
    my $sourcedmname1 = &get_macrofile_name($sourcelang, "");
    my $sourcehash1 = &parse_macrofile($macrosdir, $sourcedmname1);
    my $sourcedmname2 = &get_macrofile_name($sourcelang, "2");
    my $sourcehash2 = &parse_macrofile($macrosdir, $sourcedmname2);

    # Make sure some macros exist to be translated
    if (!$sourcehash1 && !$sourcehash2) {
	print STDERR "Error: No source macro information exists.\n";
	return 4;
    }

    # Combine the two source hashes
    my $sourcehash = &combine_hashes($sourcehash1, $sourcehash2);

    # Open the existing translation and update databases
    $translationdb = util::filename_cat($translationdir, "translation.db");
    $updatedb = util::filename_cat($translationdir, "update.db");

    # This translation is a continuation of an old one, so open the database files for it
    tie(%translationdata, "GDBM_File", $translationdb, 1, 0640);
    tie(%updatedata, "GDBM_File", $updatedb, 1, 0640);

    # Parse the target language macro files
    my $targetdmname1 = &get_macrofile_name($targetlang, "");
    my $targethash1 = &parse_macrofile($macrosdir, $targetdmname1);
    my $targetdmname2 = &get_macrofile_name($targetlang, "2");
    my $targethash2 = &parse_macrofile($macrosdir, $targetdmname2);

    # Combine the two target hashes
    my $targethash = &combine_hashes($targethash1, $targethash2);

    # Determine the macros that need translating
    my $needstranslating = &find_macros_needing_translation($sourcelang, $sourcehash,
							    $targetlang, $targethash);

    # Generates HTML code to display user interface on webpage with what needs translation
    my $targetcode = $translationdata{"*target_lang_code*"};
    print STDERR "Target language code: $targetcode\n";
    &generate_pages($sourcelang, $targetlang, $targetcode, $needstranslating);

    # Clean up
    untie %translationdata;
    untie %updatedata;

    return 0;
}


sub get_macrofile_name
{
    my ($language, $suffix) = @_;

    my $macrofilename = $language;

    # Handles cases where the macro file name is different to the language name
    $macrofilename = "port" if ($language =~ m/portuguese/);
    $macrofilename = "indo" if ($language =~ m/indonesian/);

    # Add suffix (if any) and file extension, and return
    $macrofilename = $macrofilename . $suffix . ".dm";
    return $macrofilename;
}


sub parse_macrofile
{
    my ($macrosdir, $macrofile) = @_;

    # Opens macro file or returns
    my $macropath = util::filename_cat($macrosdir, $macrofile);
    open(IN, "<$macropath") or return;

    # Initialises some local variables
    my $currpackage;
    my %macros = ();

    # Reads in contents of macro file, line by line
    while (<IN>) {
	# Check if a new package is being defined
	if (s/^package //) {
	    $currpackage = $_;
	    chomp($currpackage);
	}

	# Line contains a macro name
	elsif (/^(_\w+_)/) {
	    # gets the name of the macro ($1 contains text matched by corresponding
	    # set of parentheses in the last matched pattern within the dynamic scope
	    # which here matches (_\w+_) )
	    my $macroname = $1;
	    my $macrotext;

	    # get the first line of the macro
	    $_ =~ s/^_\w+_ *//;
	    # while there is still text of the macro to go...
	    while ($_ !~ /.*\}/) {
		# ... adds it to the macrotext variable
		$macrotext .= $_;
		$_ = <IN>;
	    }
	    $macrotext .= $_;

	    # The key consists of macro file, package name, and macro name
	    my $key = $macrofile . "::" . $currpackage . "::" . $macroname;
	    # Store the macro text in the database
	    $macros{$key} = $macrotext;
	}

	# Icon: line in format ## "sometext" ## macro ## macroname ##
	elsif (/^\#\# .*/) {
	    my $macroname = $_;
	    my $macrotext;

	    #if the macro text contains a new line will run over two lines
	    unless ($macroname =~ m/^\#\# .*\#\#/) {		
		$macroname = $_;
		chomp($macroname);
		$_ = <IN>;
		$macroname .= $_;
	    }

	    #split the image macro header on ##
	    my @names = split(/\s*\#\#\s*/, $macroname);
	    #save current contents of macroname into macrotext
	    $macrotext .= $macroname;
	    $_ = <IN>;
	    # overwrite macroname with macroname from ## ... ## ... ## HERE ###
	    $macroname = $names[(scalar @names) - 1];

	    # read in the rest of the text associated with the image macro
	    while ($_ !~ /^\s+/) {
		$macrotext .= $_;
		$_ = <IN>;
	    }

	    # key to the hash and the database
	    my $key = $macrofile . "::" . $currpackage . "::" . $macroname;
	    # print "Icon, key: $key\n text: $macrotext\n";
	    # hashes macroname and macrotext
	    $macros{$key} = $macrotext;
	}
    }
    close IN;

    return (\%macros);
}


# Combines two existing hashes of the same format together
sub combine_hashes
{
    my ($hash1, $hash2) = @_;
    my %combined = ();

    foreach $key (sort (keys %$hash1)) {
	$combined{$key} = $hash1->{$key};
    }
    foreach $key (sort (keys %$hash2)) {
	$combined{$key} = $hash2->{$key};
    }

    return (\%combined);
}


sub find_macros_needing_translation
{
    my ($sourcelang, $sourcehash, $targetlang, $targethash) = @_;

    # Find source macros whose text has changed
    foreach $sourcekey (sort keys(%$sourcehash)) {
	if ($translationdata{$sourcekey}) {
	    if ($translationdata{$sourcekey} ne $sourcehash->{$sourcekey}) {
		my $targetkey = &get_macroname_equivalent($sourcelang, $targetlang,
							  $sourcekey);
		if ($targethash->{$targetkey}) {
		    $updatedata{$targetkey} = $targethash->{$targetkey};
		}
		else {
		    $updatedata{$targetkey} = "";
		}
	    }
	}
    }

    # Clear out the translation database
    my $targetcode = $translationdata{"*target_lang_code*"};
    untie %translationdata;
    unlink($translationdb);
    %translationdata = ();
    tie(%translationdata, "GDBM_File", $translationdb, GDBM_WRCREAT, 0640);
    $translationdata{"*target_lang_code*"} = $targetcode;

    # Re-add source and target macros
    foreach $sourcekey (sort keys(%$sourcehash)) {
	$translationdata{$sourcekey} = $sourcehash->{$sourcekey};
    }
    foreach $targetkey (sort keys(%$targethash)) {
	$translationdata{$targetkey} = $targethash->{$targetkey};
    }

    my %needstranslating = ();

    # Macros that need translating are those that are in the source language macro file
    #  but no translation exists in the translation database...
    foreach $sourcekey (sort keys(%$sourcehash)) {
	my $targetkey = &get_macroname_equivalent($sourcelang, $targetlang, $sourcekey);
	if (!$translationdata{$targetkey}) {
	    $needstranslating{$sourcekey}[0] = $sourcehash->{$sourcekey};
	    $needstranslating{$sourcekey}[1] = "";
	}
    }

    # ...and those in the database of macros to update
    foreach $targetkey (sort keys(%updatedata)) {
	my $sourcekey = &get_macroname_equivalent($targetlang, $sourcelang, $targetkey);
	$needstranslating{$sourcekey}[0] = $sourcehash->{$sourcekey};
	$needstranslating{$sourcekey}[1] = $updatedata{$targetkey};
    }

    return (\%needstranslating);
}


sub get_macroname_equivalent
{
    my ($languageA, $languageB, $languageAkey) = @_;

    my $macrofile = $languageAkey;
    $macrofile =~ s/^([^:]+)::(.*)/$1/;
    my $key = $languageAkey;
    $key =~ s/^([^:])+::(.*)/$2/;

    my $languageBkey;
    if ($macrofile eq &get_macrofile_name($languageA, "")) {
	$languageBkey = &get_macrofile_name($languageB, "") . "::" . $key;
    }
    if ($macrofile eq &get_macrofile_name($languageA, "2")) {
	$languageBkey = &get_macrofile_name($languageB, "2") . "::" . $key;
    }

    return ($languageBkey);
}


# generates whole website by seperating hash of differences into groups
# of at most 15 macros and then generating a page from each group
sub generate_pages
{
    my ($sourcelang, $targetlang, $targetcode, $needstranslating) = @_;

    my %pagehash = ();

    $pageno = 1;
    $keysperpage = 0;
    $limit = 15;

    # goes through hash of differences between base language and translation
    # sorted first by whether it is from the core or auxiliary macrofile then
    # alphabetically by macroname
    foreach $sourcekey (sort keys(%$needstranslating)) {
	#nested hash. The pagehash contains a hash of all of the translation pages.
	#Then each page is itself contains a hash of all the macros on that page, where
	#each macro is associated with some macrotext and whether it was from the core
	#or auxiliary macrofile
	$pagehash->{$pageno}->{$sourcekey} = $needstranslating->{$sourcekey};
	$keysperpage++;

	# If have enough keys to generate a page with
	if ($keysperpage == $limit) {
	    #opens a file to write the HTML code to for current page
	    my $transpagefile = util::filename_cat($translationdir, $pageno . ".lang");
	    open HTMLFILE, ">$transpagefile";

	    #passes hash of keys for current page and filehandle, returns CGI data
	    &generate_form($sourcelang, $targetlang, $targetcode,
			   $pagehash->{$pageno}, \*HTMLFILE);
	    close HTMLFILE;
	    $pageno++;

	    #resets key counting variable
	    $keysperpage = 0;
	    $limit = 15;
	}
    }

    # if you exit the loop with keys still to write
    if ($keysperpage != 0) {
	# writes out the remaining keys, same format as above
	my $transpagefile = util::filename_cat($translationdir, $pageno . ".lang");
	open HTMLFILE, ">$transpagefile";
	&generate_form($sourcelang, $targetlang, $targetcode, $pagehash->{$pageno}, \*HTMLFILE);
	close HTMLFILE;
	$pageno++;
    }

    # Write the number of pages to a file for use by the receptionist
    my $numpagesfile = util::filename_cat($translationdir, "numpages.log");
    if (!open NUMPAGESLOG, ">$numpagesfile") {
	print STDERR "Error: Could not write $numpagesfile.\n";
	return 5;
    }
    print NUMPAGESLOG ($pageno - 1);
    close NUMPAGESLOG;

    # Write thankyou page for language translator once translation is complete
    my $thankyoufile = util::filename_cat($translationdir, "thankyou.lang");
    if (!open THANKYOU, ">$thankyoufile") {
	print STDERR "Error: Could not write $thankyoufile.\n";
	return 6;
    }
    print THANKYOU ("<center> _textthanks_ $targetlang _texttrans_ ",
		    "<br> _textviewtranslation_ ",
		    "<a href=\"_gwcgi_?a=p&p=home&l=$targetcode\">_texthere_</a>.",
		    # "_textgetdmfiles_ ",
		    # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . ".dm\">_texthere_</a>",
		    # " &amp; ",
		    # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . "2.dm\">_texthere_</a>.",
		    "<p></center>\n");
    close THANKYOU;
}


sub generate_form
{
    my $sourcelang = shift(@_);
    my $targetlang = shift(@_);
    my $targetcode = shift(@_);
    my $formhash = shift(@_);
    my $fh = shift(@_);

    # common gateway interface for writing the stuff to the web
    my $query = new CGI;
    my $keynamecount;

    $first = "true";
    foreach $key (sort (keys(%$formhash))) {

	my $text = $formhash->{$key}[0];
	my $default = $formhash->{$key}[1];
	# print "Text: $text Default: $default\n";

	# whole lot of formatting on strings
	# escape all the '_' with '\' so that Greenstone doesn't substitute
	# the macro names for the contents of the macro itself
	my $keyname = $key;
	$keyname  =~ s/_/\\_/g;

	$text =~ s/_/\\_/g;
	$text =~ s/(\[l=.*\])//;
	$default =~ s/_/\\_/g;
	$default =~ s/(\[l=.*\] )//;

        # must take care of image macros single and multiple line ones
	$text =~ s/(\<br\>\#\#.*\#\#\s*\<br\>).*/$1/g;
	$default =~ s/(\<br\>\#\#.*\#\#\s*\<br\>).*/$1/g;

	# format the text for displaying in the browser
	$text =~ s/[\{\}]//g unless($text =~ m/\#\# /);
	$default =~ s/[\{\}]//g unless($text =~ m/\#\# /);

	$text =~ s/(\A\n)//;
	$text =~ s/(\n\Z)//;
	$default =~ s/(\A\n)//;
	$default =~ s/(\n\Z)//;

	#tells whether is a core or auxiliary macro
	my $priority = &core_or_auxiliary_macro($sourcelang, $keyname);

	if ($first eq "true") {
	    print $fh ($query->hidden(-name=>"tlng",-default=>"$targetlang"),
		       $query->hidden(-name=>"bl",-default=>"$sourcelang"));

	    # If it is a core macro, table header displays core
	    if ($priority eq "1") {
		print $fh "</center><img src=\"_httpimg_/core.gif\">";

		#shows that have seen first macro of core type
		$first = "been";
	    }

	    # If it is an auxiliary macro, table header displays auxiliary
	    if ($priority eq "2") {
		print $fh "</center><img src=\"_httpimg_/auxiliary.gif\">";
		$first = "false";
	    }

	    print $fh ("<table cellpadding=\"10\">\n<tr>",
		       "<td><strong><center>". uc ($sourcelang) . "</center></strong></td>\n",
		       "<td><strong><center>". uc ($targetlang) . "</center></strong></td>\n",
		       "</tr>\n");
	}

	# If this is the first auxiliary macro, but not the first macro, finish the
	#   current table and start a new auxiliary table underneath
	if($priority eq "2" and $first eq "been") {
	    print $fh ("</table><br></center><img src=\"_httpimg_/auxiliary.gif\">",
		       "<table cellpadding=\"10\">\n<tr>",
		       "<td><strong><center>". uc ($sourcelang) . "</strong></center></td>\n",
		       "<td><strong><center>". uc ($targetlang) . "</strong></center></td>\n",
		       "</tr>\n");
	    $first = "false";
	}

        # so that when it is an image only get the text to translate not coding stuff aswell
	if ($text =~ m/\#\# (^|.)*/) {
	    # print STDERR "\nIcon -- Key: $keyname\nText: $text\n"; # Default: $default\n";
	    if ($& =~ m/".*"/) {
		#prints to the file two text fields, the left one contains the base language
		#macro text, the right contains the translation language macro text if any exists
		my $icontext = $&;
		$icontext =~ s/"(.*)"/$1/;
		# print "Text to translate: $icontext\n";

		if ($default =~ m/\#\# (^|.)*/) {
		    if ($& =~ m/".*"/) {
			$default = $&;
			$default =~ s/"(.*)"/$1/;
		    }
		}
		# print "Default: $default\n";

		my $remainder = $text;
		$remainder =~ s/^\#\# ([^\#]*)//;
		# print "Remainder: $remainder\n";

		$rows = 1;

		my $textbox1;
		if (1) {
		    $textbox1 = "<textarea name=\"whocaresh\" rows=\"$rows\" cols=\"50\" readonly=\"1\">";
		    $textbox1 .= "$icontext</textarea>\n";
		}
		else {
		    $textbox1 = $query->textfield(-name=>"whocares", -default=>"$icontext",
						  -size=>50, -readonly=>1);
		}
		print $fh ("<tr><td align=center>", $textbox1, "</td><td align=center>");

		my $textbox2;
		if (1) {
		    $textbox2 = "<textarea name=\"$keyname\" rows=\"$rows\" cols=\"50\">";
		    $textbox2 .= "$default</textarea><br>\n";
		}
		else {
		    $textbox2 = $query->textfield(-name=>"$keyname", -default=>"$default",
						  -size=>50);
		}
		print $fh $textbox2;

		print $fh $query->hidden(-name=>"$keyname" . "::icontext",
					 -default=>"$remainder");
		print $fh "</td></tr>\n";
	    }
	}
	# Text macro
	elsif ($text =~ m/\S+/) {
	    $text =~ s/\s+/ /g;

	    my @words = split(/ /, $text);
	    my $words = scalar(@words);
	    my $rows = sprintf("%.0f", $words/5);
	    print $fh ("<tr><td align=center>\n");
	    #determines how many rows of text are required to display the base language
	    #macro text then use this value to determine whether we should use a text area
	    #or a text field

	    # Experimenting with textfield and text area
	    # for now use exclusively textareas.

	    # if ($rows >= 0) { 
	    if (1) { 
		$rows = 1 if ($rows < 1);

		#prints to the file two text areas, the left one contains the base language
		#macro text, the right contains the translation language macro text if any exists
		    
		$text =~ s/<p>/<p>\n/g;
		$text =~ s/<br>/<br>\n/g;
		$text =~ s/<\/td>/<\/td>\n/g;

		print $fh ("<textarea name=\"whocaresh\" rows=\"$rows\" cols=\"50\" readonly=\"1\">",
			   "$text</textarea>\n",
			   "</td><td align=center>\n",
			   "<textarea name=\"$keyname\" rows=\"$rows\" cols=\"50\">", 
			   "$default</textarea><br></td></tr>\n");  
	    }
	    else {
		#prints to the file two text fields, the left one contains the base language
		#macro text, the right contains the translation language macro text if any exists
		print $fh ($query->textfield(-name=>"whocares",
					     -default=>"$text",
					     -size=>50,
					     -readonly=>1),
			   "</td><td align=center>\n",
			   $query->textfield(-name=>"$keyname" . "$keynamecount",
					     -default=>"$default",
					     -size=>50),
			   "<br></td></tr>\n");
	    }
	}
    }

    #finishes table and adds a SUBMIT CHANGES option on the end
    print $fh ("</table><br><center>\n",
	       "<table><tr><td align=left width=\"50%\">",
	       $query->reset("RESET FORM"),
	       "</td><td align=right width=\"50%\">",
	       $query->submit($pageno, "_textsubmit_"),
	       "</td></tr><tr><td width=\"50%\"></td><td align=right width=\"50%\">",
	       "<br> _textsubmittext_ <br> _textviewtranslation_ ",
	       "<a href=\"_gwcgi_?a=p&p=home&l=$targetcode\">_texthere_</a>.",
	       # "_textgetdmfiles_ ",
	       # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . ".dm\">_texthere_</a>",
	       # " &amp; ",
	       # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . "2.dm\">_texthere_</a>.",
	       "</td></tr></table>");
}


sub core_or_auxiliary_macro
{
    my ($language, $sourcekey) = @_;

    my $macrofile = $sourcekey;
    $macrofile =~ s/^([^:]+)::(.*)/$1/;

    if ($macrofile eq ($language . ".dm")) {
	return "1";  # Core macro
    }
    if ($macrofile eq ($language . "2.dm")) {
	return "2";  # Auxiliary macro
    }
}


sub display_hash
{
    my ($hash) = @_;

    foreach $key (sort (keys %$hash)) {
	print $key . "\n";
	print $hash->{$key} . "\n";
    }
}

exit &main(@ARGV);
