<?php
/**
 * Implements support to localization. Currently is supported english, spanish and portuguese.
 *
 * @author Camilo Carromeu <camilo@carromeu.com>
 * @category class
 * @package core
 * @subpackage util
 * @copyright 2005-2017 Titan Framework
 * @license http://www.titanframework.com/license/ BSD License (3 Clause)
 * @see Locale, Instance
 */
class Localization
{
	static private $locale = FALSE;

	private $language = 'en_US';

	private $enabled = array ('en_US');

	private $packs = array ();

	static private $supported = array (
		'pt_BR' => 'Português',
		'es_ES' => 'Espanol',
		'en_US' => 'English'
	);

	static private $negotiate = array (
		'pt'	=> 'pt_BR',
		'pt-br' => 'pt_BR',
		'es'	=> 'es_ES',
		'es-mx' => 'es_ES',
		'es-gt' => 'es_ES',
		'es-cr' => 'es_ES',
		'es-do' => 'es_ES',
		'es-pa' => 'es_ES',
		'es-ve' => 'es_ES',
		'es-co' => 'es_ES',
		'es-pe' => 'es_ES',
		'es-ar' => 'es_ES',
		'es-ec' => 'es_ES',
		'es-cl' => 'es_ES',
		'es-uy' => 'es_ES',
		'es-py' => 'es_ES',
		'es-bo' => 'es_ES',
		'es-sv' => 'es_ES',
		'es-hn' => 'es_ES',
		'es-ni' => 'es_ES',
		'es-pr' => 'es_ES',
		'en'	=> 'en_US',
		'en-us' => 'en_US',
		'en-gb' => 'en_US',
		'en-au' => 'en_US',
		'en-ca' => 'en_US',
		'en-nz' => 'en_US',
		'en-ie' => 'en_US',
		'en-za' => 'en_US',
		'en-jm' => 'en_US',
		'en-bz' => 'en_US',
		'en-tt' => 'en_US'
	);

	static private $winLocales = array (
		'pt_BR'	=> 'ptb',
		'en_US' => 'usa',
		'es_ES' => 'esp'
	);

	private $forComponent = '';

	private final function __construct ()
	{
		$languages = Instance::singleton ()->getLanguages ();

		$array = array ();
		foreach ($languages as $trash => $language)
		{
			$language = trim ($language);

			if (!self::supports ($language))
				continue;

			$array [] = $language;
		}

		if ((int) sizeof ($array))
			$this->enabled = $array;
		else
			$array = $this->enabled;

		if (isset ($_GET ['language']))
			$language = trim ($_GET ['language']);
		if (isset ($_COOKIE ['_TITAN_LOCALE_']) && trim ($_COOKIE ['_TITAN_LOCALE_']) != '')
			$language = trim ($_COOKIE ['_TITAN_LOCALE_']);
		else
			$language = self::negotiatedLanguage ();

		if (trim ($language) == '' || !in_array ($language, $array))
			$language = array_shift ($array);

		$this->setLanguage ($language);
	}

	static public function singleton ()
	{
		if (self::$locale !== FALSE)
			return self::$locale;

		$class = __CLASS__;

		self::$locale = new $class ();

		return self::$locale;
	}

	public function setLanguage ($language)
	{
		$language = trim ($language);

		if (!self::supports ($language))
			return FALSE;

		setcookie ('_TITAN_LOCALE_', $language);

		$this->language = $language;

		putenv ('LANG='. $this->getLocale () .'.UTF8');
		putenv ('LANGUAGE='. $this->getLocale () .'.UTF8');

		if (setlocale (LC_ALL, $this->getLocale () .'.utf8') === FALSE && setlocale (LC_ALL, $this->getLocale ()) === FALSE)
			toLog ('Impossible to set system locale ['. $this->getLocale () .'].');

		return TRUE;
	}

	public function getLanguage ()
	{
		return $this->language;
	}

	public function getLocale ()
	{
		if (stristr (PHP_OS, 'win') !== FALSE)
			return self::$winLocales [$this->getLanguage ()];

		return $this->getLanguage ();
	}

	public function translate ($array)
	{
		if (!sizeof ($array))
			return '';

		$message = array_shift ($array);

		$pack = $this->getLanguagePack ();

		if (isset ($pack [$message]))
			$message = $pack [$message];

		foreach ($array as $key => $value)
			$message = str_replace ('['. ($key + 1) .']', $value, $message);

		return $message;
	}

	private function getLanguagePack ($language = FALSE)
	{
		if ($language === FALSE)
			$language = $this->language;

		$language = trim ($language);

		$section = Business::singleton ()->getSection (Section::TCURRENT);

		if (array_key_exists ($language, $this->packs) && $this->forComponent == $section->getComponent ())
			return $this->packs [$language];

		$this->forComponent = $section->getComponent ();

		$array = array ();

		$files = array (Instance::singleton ()->getCorePath ().'locale/i18n/'. $language .'.xml', $section->getCompPath () .'_i18n/'. $language .'.xml');

		$packs = array (Instance::singleton ()->getCachePath () .'i18n/'. $language .'.php', Instance::singleton ()->getCachePath () .'i18n/'. $language .'-component-'. fileName ($section->getComponent ()) .'.php');

		foreach (Instance::singleton ()->getTypes () as $type => $path)
		{
			$files [] = $path .'_i18n/'. $language .'.xml';
			$packs [] = Instance::singleton ()->getCachePath () .'i18n/'. $language .'-type-'. fileName ($type) .'.php';
		}

		if (Instance::singleton ()->onDebugMode ())
			foreach (Instance::singleton ()->getTools () as $tool => $path)
			{
				$path = dirname (realpath ($path));

				$files [] = $path . DIRECTORY_SEPARATOR .'_i18n'. DIRECTORY_SEPARATOR . $language .'.xml';
				$packs [] = Instance::singleton ()->getCachePath () .'i18n'. DIRECTORY_SEPARATOR . $language .'-tool-'. fileName ($tool) .'.php';
			}

		foreach ($files as $key => $file)
		{
			if (!file_exists ($file))
				continue;

			$pack = $packs [$key];

			if (file_exists ($pack) && @filemtime ($pack) > @filemtime ($file))
			{
				$array = array_merge ($array, include $pack);

				continue;
			}

			$xml = new Xml ($file);

			$aux = $xml->getArray ();

			if (!isset ($aux ['i18n'][0]['message']) || !sizeof ($aux ['i18n'][0]['message']))
				continue;

			$aFile = array ();

			foreach ($aux ['i18n'][0]['message'] as $trash => $message)
			{
				if (!is_array ($message) || !array_key_exists ('from', $message) || !array_key_exists ('to', $message))
					continue;

				$aFile [$message ['from']] = $message ['to'];
			}

			$array = array_merge ($array, $aFile);

			$content  = "<?php \n";
			$content .= "/* \n";
			$content .= " * Titan Framework \n";
			$content .= " * Cache of Language Pack (i18n) \n";
			$content .= " * Generated at ". date ('d-m-Y H:i:s') ." \n";
			$content .= " */ \n\n";
			$content .= "return ". var_export ($aFile, TRUE) .";";

			$path = Instance::singleton ()->getCachePath () .'i18n/';

			if (!file_exists ($path) && !@mkdir ($path, 0777))
				toLog ('Impossible to create directory ['. $path .'].');
			elseif (file_put_contents ($pack, $content) === FALSE)
				toLog ('Impossible to create file ['. $pack .'].');
		}

		$this->packs [$language] = $array;

		return $array;
	}

	private static function supports ($language)
	{
		return array_key_exists ($language, self::$supported);
	}

	public static function negotiatedLanguage ($forConversion = '')
	{
		if ($forConversion != '' && array_key_exists ($forConversion, self::$negotiate))
			return self::$negotiate [$forConversion];

		if (!array_key_exists ('HTTP_ACCEPT_LANGUAGE', $_SERVER))
			return '';

		$client = str_replace (array (',', ';'), '#', strtolower ($_SERVER ['HTTP_ACCEPT_LANGUAGE']));

		$languages = explode ('#', $client);

		foreach ($languages as $trash => $lang)
			if (array_key_exists ($lang, self::$negotiate))
				return self::$negotiate [$lang];

		return '';
	}

	public function getAvaliableLanguages ()
	{
		$array = array ();
		foreach (self::$supported as $language => $label)
			if (in_array ($language, $this->enabled))
				$array [$language] = $label;

		return $array;
	}
}