#!/usr/bin/env php
<?php

/**
 * Contao Open Source CMS
 * 
 * Copyright (C) 2005-2012 Leo Feyer
 * 
 * @package Core
 * @link    http://contao.org
 * @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL
 */


/**
 * Define the TL_ROOT constant
 */
define('TL_ROOT', dirname(dirname(__DIR__)));


/**
 * Prepare the language files for Transifex.
 *
 * @package   Core
 * @author    Leo Feyer <https://github.com/leofeyer>
 * @copyright Leo Feyer 2011-2012
 */
class Transifex
{

	/**
	 * Dispatch the request
	 */
	public function run()
	{
		global $argv;

		if (!isset($argv[1]) || !in_array($argv[1], get_class_methods($this)))
		{
			$this->help();
		}
		else
		{
			$this->$argv[1]();
		}
	}


	/**
	 * Show the help text
	 */
	protected function help()
	{
		echo "\n";
		echo "  \033[1;33mPrepare the Contao language files for Transifex\033[0m\n";
		echo "  Usage: system/bin/transifex <command>\n";
		echo "\n";
		echo "  \033[1;33m<command>\033[0m\n";
		echo "  \033[0;36msetup\033[0m         Create the source translation files\n";
		echo "  \033[0;36mimport\033[0m        Convert the Transifex files into Contao files\n";
		echo "  \033[0;36mexport\033[0m        Export and push an existing translation\n";
		echo "  \033[0;36mpurge\033[0m         Remove and reimport all Contao language files\n";
		echo "  \033[0;36mhelp\033[0m          Show this help text\n";
		echo "\n";
		echo "  The \"push\" command will execute \"setup\" and the \"pull\" command will execute \"import\" automatically.\n";
		echo "\n";
	}


	/**
	 * Setup the .xlf and config files for the Transifex client
	 * 
	 * @param boolean $quiet If true, do not output any status messages
	 */
	protected function setup($quiet=false)
	{
		// Purge the target folder
		$this->rrdir(TL_ROOT . '/.tx/source/en');
		mkdir(TL_ROOT . '/.tx/source/en');

		// Create the .tx/config file
		$fh = fopen(TL_ROOT . '/.tx/config', 'wb');
		fputs($fh, "[main]\nhost = https://www.transifex.com\ntype = XLIFF\n");

		// Scan the modules folder
		foreach ($this->scandir(TL_ROOT . '/system/modules') as $module)
		{
			if (!is_dir(TL_ROOT . '/system/modules/' . $module . '/languages/en'))
			{
				continue;
			}

			// Create the subdirectory
			if (!is_dir(TL_ROOT . '/.tx/source/en/' . $module))
			{
				mkdir(TL_ROOT . '/.tx/source/en/' . $module);
			}

			// Scan the language files
			foreach ($this->scandir(TL_ROOT . '/system/modules/' . $module . '/languages/en') as $file)
			{
				if (substr($file, -4) != '.php')
				{
					continue;
				}

				// Reset the TL_LANG array before the include
				$GLOBALS['TL_LANG'] = array();
				include TL_ROOT . '/system/modules/' . $module . '/languages/en/' . $file;
				$file = basename($file, '.php');

				// Create the XML document
				$xml = new DOMDocument('1.0', 'UTF-8');
				$xml->formatOutput = true;

				$xliff = $xml->createElement('xliff');
				$xliff->setAttribute('version', '1.1');
				$xml->appendChild($xliff);

				$xfile = $xml->createElement('file');
				$xfile->setAttribute('datatype', 'php');
				$xfile->setAttribute('original', 'system/modules/' . $module . '/languages/en/' . $file . '.php');
				$xfile->setAttribute('source-language', 'en');
				$xliff->appendChild($xfile);

				$xbody = $xml->createElement('body');
				$xfile->appendChild($xbody);

				// Add the labels
				foreach ($GLOBALS['TL_LANG'] as $category=>$labels)
				{
					foreach ($labels as $key=>$label)
					{
						if (is_array($label)) // headline and explanation
						{
							foreach ($label as $i=>$text)
							{
								if (is_array($text)) // multiple explanations
								{
									foreach ($text as $j=>$subtext)
									{
										$trans = $xml->createElement('trans-unit');
										$trans->setAttribute('id', "$category.$key.$i.$j");
										$xbody->appendChild($trans);

										$source = $xml->createElement('source');
										$source->appendChild($xml->createTextNode($subtext));
										$trans->appendChild($source);
									}
								}
								else
								{
									$trans = $xml->createElement('trans-unit');
									$trans->setAttribute('id', "$category.$key.$i");
									$xbody->appendChild($trans);

									$source = $xml->createElement('source');
									$source->appendChild($xml->createTextNode($text));
									$trans->appendChild($source);
								}
							}
						}
						else
						{
							$trans = $xml->createElement('trans-unit');
							$trans->setAttribute('id', "$category.$key");
							$xbody->appendChild($trans);

							$source = $xml->createElement('source');
							$source->appendChild($xml->createTextNode($label));
							$trans->appendChild($source);
						}
					}
				}

				// Save the XML document
				$xml->save(TL_ROOT . '/.tx/source/en/' . $module . '/' . $file . '.xlf');

				// Confirm
				if (!$quiet)
				{
					echo "  Created .tx/source/en/$module/$file.xlf\n";
				}

				// Add an entry to the .tx/config file
				fputs($fh, "\n[contao.$module-$file]");
				fputs($fh, "\nsource_file = .tx/source/en/$module/$file.xlf");
				fputs($fh, "\nsource_lang = en");
				fputs($fh, "\nfile_filter = .tx/source/<lang>/$module/$file.xlf\n");
			}
		}

		// Close the .tx/config file
		fclose($fh);
	}


	/**
	 * Create the Contao language files from the Transifex .xlf files
	 */
	protected function import()
	{
		global $argv;
		$languages = array();

		// Determine which languages to import
		if (isset($argv[2]))
		{
			if (is_dir(TL_ROOT . '/.tx/source/' . $argv[2]))
			{
				$languages[] = $argv[2];
			}
		}
		else
		{
			foreach ($this->scandir(TL_ROOT . '/.tx/source') as $language)
			{
				$languages[] = $language;
			}
		}

		// Process the selected languages
		foreach ($languages as $language)
		{
			foreach ($this->scandir(TL_ROOT . '/.tx/source/' . $language) as $module)
			{
				// Create the language folder
				if (!is_dir(TL_ROOT . '/system/modules/' . $module . '/languages/' . $language))
				{
					mkdir(TL_ROOT . '/system/modules/' . $module . '/languages/' . $language);
				}

				foreach ($this->scandir(TL_ROOT . '/.tx/source/' . $language . '/' . $module) as $file)
				{
					if (substr($file, -4) != '.xlf')
					{
						continue;
					}

					// Read the .xlf file
					$xml = new DOMDocument();
					$xml->preserveWhiteSpace = false;
					$xml->load(TL_ROOT . '/.tx/source/' . $language . '/' . $module . '/' . $file);
					$units = $xml->getElementsByTagName('trans-unit');
					$file = basename($file, '.xlf');

					// Create the .php file
					$fh = fopen(TL_ROOT . '/system/modules/' . $module . '/languages/' . $language . '/' . $file . '.php', 'wb');

					// Add a short header with links to transifex.com
					fputs($fh, "<?php\n\n");
					fputs($fh, "/**\n");
					fputs($fh, " * Contao Open Source CMS\n");
					fputs($fh, " * \n");
					fputs($fh, " * Copyright (C) 2005-2012 Leo Feyer\n");
					fputs($fh, " * \n");
					fputs($fh, " * Core translations are managed using Transifex. To create a new translation\n");
					fputs($fh, " * or to help to maintain an existing one, please register at transifex.com.\n");
					fputs($fh, " * \n");
					fputs($fh, " * @link http://help.transifex.com/intro/translating.html\n");
					fputs($fh, " * @link https://www.transifex.com/projects/p/contao/language/$language/\n");
					fputs($fh, " * \n");
					fputs($fh, " * @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL\n");
					fputs($fh, " */\n\n");

					// Add the labels
					foreach ($units as $unit)
					{
						$node = ($language == 'en') ? $unit->firstChild : $unit->firstChild->nextSibling;

						if ($node === null)
						{
							continue;
						}

						$value = str_replace("\n", '\n', $node->nodeValue);

						// Quote the value depending on whether there are line breaks
						if (strpos($value, '\n') !== false)
						{
							$value = '"' . str_replace('"', '\\"', $value) . '"';
						}
						else
						{
							$value = "'" . str_replace("'", "\\'", $value) . "'";
						}

						// Some closing </em> tags oddly have an extra space in
						if (strpos($value, '</ em>') !== false)
						{
							$value = str_replace('</ em>', '</em>', $value);
						}

						$chunks = explode('.', $unit->getAttribute('id'));

						// Handle keys with dots
						if (preg_match('/tl_layout\.(reset|layout|responsive|tinymce)\.css\./', $unit->getAttribute('id')))
						{
							$chunks = array($chunks[0], $chunks[1] . '.' . $chunks[2], $chunks[3]);
						}

						switch (count($chunks))
						{
							case 2:
								fputs($fh, "\$GLOBALS['TL_LANG']['" . $chunks[0] . "'][" . $this->quote($chunks[1]) . "] = $value;\n");
								break;

							case 3:
								fputs($fh, "\$GLOBALS['TL_LANG']['" . $chunks[0] . "'][" . $this->quote($chunks[1]) . "][" . $this->quote($chunks[2]) . "] = $value;\n");
								break;

							case 4:
								fputs($fh, "\$GLOBALS['TL_LANG']['" . $chunks[0] . "'][" . $this->quote($chunks[1]) . "][" . $this->quote($chunks[2]) . "][" . $this->quote($chunks[3]) . "] = $value;\n");
								break;
						}
					}

					// Close the file
					fclose($fh);
					echo "  Created system/modules/$module/languages/$language/$file.php\n";
				}
			}
		}
	}


	/**
	 * Export and push an existing translation
	 */
	protected function export()
	{
		global $argv;

		// The language code is required
		if (!isset($argv[2]))
		{
			die("  Please provide the language code as argument\n");
		}

		$this->setup(true);

		// Purge the target folder
		$this->rrdir(TL_ROOT . '/.tx/source/' . $argv[2]);
		mkdir(TL_ROOT . '/.tx/source/' . $argv[2]);

		// Scan the modules folder
		foreach ($this->scandir(TL_ROOT . '/system/modules') as $module)
		{
			// Skip if the source language does not exist
			if (!is_dir(TL_ROOT . '/system/modules/' . $module . '/languages/en'))
			{
				continue;
			}

			// Create the subdirectory
			if (!is_dir(TL_ROOT . '/.tx/source/' . $argv[2] . '/' . $module))
			{
				mkdir(TL_ROOT . '/.tx/source/' . $argv[2] . '/' . $module);
			}

			// Scan the source language files
			foreach ($this->scandir(TL_ROOT . '/system/modules/' . $module . '/languages/en') as $file)
			{
				if (substr($file, -4) != '.php')
				{
					continue;
				}

				// The file has not been translated yet
				if (!file_exists(TL_ROOT . '/system/modules/' . $module . '/languages/' . $argv[2] . '/' . $file))
				{
					continue;
				}

				$file = basename($file, '.php');

				// Read the source file
				$english = new DOMDocument();
				$english->preserveWhiteSpace = false;
				$english->load(TL_ROOT . '/.tx/source/en/' . $module . '/' . $file . '.xlf');

				// Prepare for XPath
				$xpath = new DOMXPath($english);

				// Create the XML document
				$xml = new DOMDocument('1.0', 'UTF-8');
				$xml->formatOutput = true;

				$xliff = $xml->createElement('xliff');
				$xliff->setAttribute('version', '1.1');
				$xml->appendChild($xliff);

				$xfile = $xml->createElement('file');
				$xfile->setAttribute('datatype', 'php');
				$xfile->setAttribute('original', 'system/modules/' . $module . '/languages/en/' . $file . '.php');
				$xfile->setAttribute('source-language', 'en');
				$xliff->appendChild($xfile);

				$xbody = $xml->createElement('body');
				$xfile->appendChild($xbody);

				// Reset the TL_LANG array before the include
				$GLOBALS['TL_LANG'] = array();
				include TL_ROOT . '/system/modules/' . $module . '/languages/' . $argv[2] . '/' . $file . '.php';

				// Add the labels
				foreach ($GLOBALS['TL_LANG'] as $category=>$labels)
				{
					foreach ($labels as $key=>$label)
					{
						if (is_array($label)) // headline and explanation
						{
							foreach ($label as $i=>$text)
							{
								if (is_array($text)) // multiple explanations
								{
									foreach ($text as $j=>$subtext)
									{
										$id = "$category.$key.$i.$j";

										// Check whether the string exists in the source translation
										if (($original = $xpath->query("//*[@id='$id']")->item(0)) !== null)
										{
											$trans = $xml->createElement('trans-unit');
											$trans->setAttribute('id', $id);
											$xbody->appendChild($trans);

											$source = $xml->createElement('source');
											$source->appendChild($xml->createTextNode($original->nodeValue));
											$trans->appendChild($source);

											$target = $xml->createElement('target');
											$target->appendChild($xml->createTextNode($subtext));
											$trans->appendChild($target);
										}
									}
								}
								else
								{
									$id = "$category.$key.$i";

									// Check whether the string exists in the source translation
									if (($original = $xpath->query("//*[@id='$id']")->item(0)) !== null)
									{
										$trans = $xml->createElement('trans-unit');
										$trans->setAttribute('id', $id);
										$xbody->appendChild($trans);

										$source = $xml->createElement('source');
										$source->appendChild($xml->createTextNode($original->nodeValue));
										$trans->appendChild($source);

										$target = $xml->createElement('target');
										$target->appendChild($xml->createTextNode($text));
										$trans->appendChild($target);
									}
								}
							}
						}
						else
						{
							$id = "$category.$key";

							// Check whether the string exists in the source translation
							if (($original = $xpath->query("//*[@id='$id']")->item(0)) !== null)
							{
								$trans = $xml->createElement('trans-unit');
								$trans->setAttribute('id', $id);
								$xbody->appendChild($trans);

								$source = $xml->createElement('source');
								$source->appendChild($xml->createTextNode($original->nodeValue));
								$trans->appendChild($source);

								$target = $xml->createElement('target');
								$target->appendChild($xml->createTextNode($label));
								$trans->appendChild($target);
							}
						}
					}
				}

				// Save the XML document
				$xml->save(TL_ROOT . '/.tx/source/' . $argv[2] . '/' . $module . '/' . $file . '.xlf');
				echo "  Created .tx/source/{$argv[2]}/$module/$file.xlf\n";

				// Push the resouce to transifex.com
				passthru('tx push --translations --language=' . $argv[2] . ' --resource=contao.' . $module . '-' . $file);
			}
		}
	}


	/**
	 * Remove the Contao language files
	 */
	protected function purge()
	{
		global $argv;

		foreach ($this->scandir(TL_ROOT . '/system/modules') as $module)
		{
			if (!is_dir(TL_ROOT . '/system/modules/' . $module . '/languages'))
			{
				continue;
			}

			if (isset($argv[2]))
			{
				for ($i=2; $i<count($argv); $i++)
				{
					if (is_dir(TL_ROOT . '/system/modules/' . $module . '/languages/' . $argv[$i]))
					{
						$this->rrdir(TL_ROOT . '/system/modules/' . $module . '/languages/' . $argv[$i]);
					}
				}
			}
			else
			{
				foreach ($this->scandir(TL_ROOT . '/system/modules/' . $module . '/languages') as $language)
				{
					$this->rrdir(TL_ROOT . '/system/modules/' . $module . '/languages/' . $language);
				}
			}
		}
	}


	/**
	 * Return the contents of a directory without "." and ".."
	 * 
	 * @param string $dir The directory path
	 * 
	 * @return array The directory content
	 */
	protected function scandir($dir)
	{
		$return = array();

		foreach (scandir($dir) as $file)
		{
			if ($file != '.' && $file != '..' && $file != '.DS_Store')
			{
				$return[] = $file;
			}
		}

		return $return;
	}


	/**
	 * Quote a key if it is not numeric
	 * 
	 * @param string $key The string representation of the key
	 * 
	 * @return mixed The quoted string or numeric integer
	 */
	protected function quote($key)
	{
		if ($key === '0')
		{
			return 0;
		}
		elseif (is_numeric($key))
		{
			return intval($key);
		}
		else
		{
			return "'$key'";
		}
	}


	/**
	 * Recursively remove a directory
	 * 
	 * @param string $dir The directory path
	 */
	protected function rrdir($dir)
	{
		if (!is_dir($dir))
		{
			return;
		}

		foreach ($this->scandir($dir) as $file)
		{
			if (is_dir("$dir/$file"))
			{
				$this->rrdir("$dir/$file");
			}
			else
			{
				unlink("$dir/$file");
			}
		}

		rmdir($dir);
	}
}

$objTransifex = new Transifex();
$objTransifex->run();
