<?PHP
/* Merge extract Javascript V2.0 20210403 hman                           */
/* 20210328 V1.5 Check with Levenshtein's algorithm (similarity) added   */
/* 20210403 V2.0 Check for different endings added                       */
/*                                                                       */
/* Merges the associative arrays of tra_dedup and a given language.js    */
/* with preserving comments and some integrity checks performed.         */
/*                                                                       */

function report_levenshtein() {
/* Check the keys in both arrays with Levenshtein's algorithm for similarity, */
/* Like added/ommited full stops, colons, question marks etc. Levenshtein reports */
/* the amount of characters that have to be changed to make strings identical. */
/* Very ressource intensive! */

global $fe;
global $lang, $lang_custom;

	echo "== Reporting results of Levenshtein's algorithm >0 && <=4 && < strlen/2\n";
	fwrite ($fe, "== Reporting results of Levenshtein's algorithm >0 && <=4 && < 2*strlen\n");

	/* line counter in lang_custom */
	$lcline = 1;
	foreach ($lang_custom as $ckey => $cvalue) {
		$lcline++;
		if (!empty($cvalue)) {
			/* Trim tab, comma and quotes */
			$ckey = trim(ltrim($ckey,"\t"),"\"");
			$cvalue = trim(rtrim(ltrim($cvalue,"\t"),","),"\"");
			/* line counter in lang */
			$lline = 1;
			foreach ($lang as $lkey => $lvalue) {
			$lline++;
				if (!empty($cvalue)) {
					/* Trim tab, comma and quotes */
					$lkey = trim(ltrim($lkey,"\t"),"\"");
					$lvalue = trim(rtrim(ltrim($lvalue,"\t"),","),"\"");
					$ls = levenshtein($ckey, $lkey);
					$sub2 = substr($lkey,0,2);
					/* Check more frequent condition first, try to gain a tiny little bit */
					/* of speed. Also add is faster thany multiply. Ignore comments */
					if ( ($ls <= 4) && ($ls > 0) && (strlen($ckey) > $ls+$ls) && ($sub2 != "//") &&
						($sub2 != "/*") ) {
						echo "--Levenshtein line " . $lcline . "," . $lline . ": " . $ls . " key1='" .
							$ckey .	"' key2='" . $lkey . "'\n"; 
						fwrite ($fe, "--Levenshtein line " . $lcline . "," . $lline . ": " . $ls . " key1='" .
							$ckey .	"' key2='" . $lkey . "'\n"); 
					}
				}
				/* end if */
			}
			/* end foreach */
		}
		/* end if */
	}
	/* end foreach */
	echo "== Report completed.\n";
	fwrite ($fe, "== Report completed.\n"); 
}
/* end function */

function report_diffending() {
/* Check the keys and values for differences in ending , like missing */
/* or added full stops, spaces, colons, semicolons and commas. */

global $fe;
global $lang, $lang_custom;

	echo "\n== Reporting differences in endings\n";
	fwrite ($fe, "\n== Reporting differences in endings\n");

	/* line counter in lang_custom */
	$lcline = 1;
	foreach ($lang_custom as $ckey => $cvalue) {
		$lcline++;
		if (!empty($cvalue)) {
			/* Trim tab and quotes */
			$ckey = trim(ltrim($ckey,"\t"),"\"");
			$cvalue = trim(rtrim(ltrim($cvalue,"\t"),","),"\"");
			$subck2 = substr($ckey,-2);
			$subcv2 = substr($cvalue,-2);
			$sub = substr($ckey,0,2);

			/* Check last char if last 2 chars contain punctuation marks or spaces and differ */
			if ( ( (strcspn($subck2, " .:;,?!") < 2) || (strcspn($subcv2, " .:;,?!") < 2) ) &&
				(substr($cvalue, -1) != substr($ckey, -1)) && ($sub != "//") && ($sub != "/*") ) {
				echo "--Different ending in line " . $lcline . " '". $subck2 . "' != '" . $subcv2 . "'\n";
				fwrite ($fe, "--Different ending in line " . $lcline . " '". $subck2 . "' != '" . $subcv2 . "'\n");
			}
		}
		/* end if */
	}
	/* end foreach */
	echo "== Report completed.\n";
	fwrite ($fe, "== Report completed.\n"); 
}

/* end function */

function chk_file($filename) {
/* Check a given file for incorrect syntax (empty/missing key or value). Then */
/* check for duplicates. PHP would only overwrite old key/value pairs, this */
/* function finds them, so they can be manually compared and eliminated */

global $fe;
global $lang, $lang_custom;

	echo "== Checking duplicates in file ". $filename . "\n";
	fwrite ($fe, "== Checking duplicates in file ". $filename . "\n");

	/* read the input file given, skip \n */
	$fl = file($filename,FILE_IGNORE_NEW_LINES);

	if (!$fl) {
		echo "-- Error! File " . $filename . " cannot be opened!\n";
		return;
	}

	/* build one array with key/value and one with line numbers */
	$array = array();
	$linenos = array();

	$linecount = 0;

	foreach ($fl as $line)	{	
		$linecount++;

		if ($line=="") {
			/* remember blank lines for structure. Do not omit linecount! */
			$key="//BLANK".$linecount;
			$value="";
		}

		if ( ($line != "") && ($line != "\n") && ($line != "lang = {") && ($line != "lang_extract_raw = {") && 
			($line != "};") ) {
			list($key, $value) = explode(' : ', $line, 2) + array(NULL, NULL);
			/* Check for syntax errors (empty key or value) */
			if (empty($key)) {
				echo "-- Error! Empty key in line " . $linecount . " for value='" . $value."'\n";
				fwrite ($fe, "-- Error! Empty key in line " . $linecount . " for value='" . $value."'\n");
			}
			if ( (empty($value)) && (substr($key,0,2) != "//") && (substr($key,0,2) != "/*") ) {
 				echo "-- Error! Empty value in line " . $linecount . " for key='" . $key."'\n";
				fwrite ($fe, "-- Error! Empty value in line " . $linecount . " for key='" . $key."'\n");
			}

			/* Check for double entries */
			if (array_key_exists ($key, $array)) {
				/* previous definition */
				echo "-- Duplicate: key in line " . $linenos[$key] ."\n";
				echo $key . " : ". $array[$key] . "\n";
				/* duplicate definition */
				echo "   is identical with line " . $linecount . ".\n";
				echo $key . " : ". $value . "\n\n";
				/* same as above, log in file */
				fwrite ($fe, "-- Duplicate: key in line " . $linenos[$key] ."\n");
				fwrite ($fe,  $key . " : ". $array[$key] . "\n");
				fwrite ($fe, "   is identical with line " . $linecount . ".\n");
				fwrite ($fe,  $key . " : ". $value . "\n\n");
			}
			
		}
		/* end if */

		/* NULL value means comment, remember */
		/* remember keys and their line numbers */
		$array[$key] = $value;
		$linenos[$key] = $linecount;

	}
	/* end foreach */
	echo "== Check completed.\n";
	fwrite ($fe, "== Check completed.\n"); 

	return $array;
}
/* end function */

function outputarr($outputfile, $arrout, $comment) {
/* This function outputs the given array to the given output file, introducing */
/* a comment in the second line */

	if ( file_exists($outputfile) ) {
		echo "-- Error! File " . $outputfile . " already exists! Skipping.\n";
		return;
	}
	$fo = fopen($outputfile, "a+");

	if (!$fo or feof($fo)) {
		echo "-- Error! Outputfile " . $outputfile . " is damaged! Exiting.\n";
		exit;
	}

	/* write header */
	$output = "/* " . $comment . " */\n";
	fwrite($fo,$output);

	$output = "\nlang = {\n";
	fwrite($fo,$output);

	foreach ($arrout as $key => $value) {
		/* Either comment, blank line or key/value pair */
		if (empty($value)) {
			if (substr($key,0,7) == "//BLANK") 
				$outputstring = "\n";
			else
				$outputstring = $key."\n";
		}
		else
	    		$outputstring= $key." : ".$value."\n";
    		fwrite($fo, $outputstring);
	}

	/* write footer */
	$output = "};\n";
	fwrite($fo,$output);

	/* flush cache and close */
	fflush ($fo);
	if (!fclose($fo)) {
   		echo "-- Error! Output file " . $outputfile . " is not closeable! Exiting.\n";
		exit;
	}
}
/* end function */

/* Main program */
global $fe;
global $lang, $lang_custom;

echo "\n";
echo "=== Merge extract Javascript language V2.0 20210403 hman\n\n";
echo "Merges the associative arrays of tra_dedup_js.txt und language.js of a Tiki\n";
echo "localization. To be run in the directory containing the localization files.\n";
echo "Bugfixed version with array_replace instead of array_merge and syntax check.\n\n";

if ( file_exists("./duplicat_error_lang_js.txt") ) {
/* tell user file pre-exists */
	echo "-- Error! File ./duplicate_error_lang_js.txt already exists! Appending.\n";
}
/* file for duplicate error logging */
$fe = fopen("./duplicate_error_lang_js.txt", "a+");

/* Check both files for double key definitions */
$lang = chk_file("./tra_dedup.js.txt");
$lang_custom = chk_file("./language.js");

/* Separation just for looks */
echo "\n";
fwrite ($fe, "\n");

/* Report the results of Levenshtein's algorithm */
report_levenshtein();
/* Report the results of searching for different endings */
report_diffending();

/* Now perform the four possible array combinations to enable the user to inspect the outcome */

echo "\n== Merging ./tra_dedup.js.txt & ./language.js\n";
/* DO NOT use array_merge, because it won't handle numeric keys correctly! */
$merge = array_replace($lang, $lang_custom);
outputarr ("./merge_lang_js.txt", $merge, "Merged arrays of tra_dedup.js.txt and language.js to replace language.js.");

echo "== Finding new, added key/value pairs\n";
$diff = array_diff_key($lang_custom, $lang);
outputarr ("./new_lang_js.txt", $diff, "Key/value pairs that do not exist in tra_dedup.js.txt and were added in language.js.");

echo "== Finding changes in key/value pairs\n";
$diff = array_intersect_key($lang_custom, $lang);
outputarr ("./changed_lang_js.txt", $diff, "Key/value pairs from tra_dedup.js.txt that were changed in language.js.");

echo "== Finding unchanged key/value pairs\n";
$diff = array_diff_key($lang, $lang_custom);
outputarr ("./unchanged_lang_js.txt", $diff, "Key/value pairs from tra_dedup.js.txt that were not touched in language.js.");


fflush ($fe);
if (!fclose($fe)) {
	echo "-- Error! Output file ./duplicate_error_lang_js.txt is not closeable! Exiting.\n";
	exit;
}

/* end of program */
?>
