<?php
/**
* @version	$Id: minify_helper.php 13889 2010-08-29 09:30:02Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2010 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	class MinifyHelper extends kHelper {

		/**
		 * Debug mode mark
		 *
		 * @var bool
		 */
		var $debugMode = false;

		/**
		 * Path to compress information file
		 *
		 * @var string
		 */
		var $infoFile = '';

		/**
		 * Compress information
		 *
		 * @var Array
		 */
		var $compressInfo = Array ();

		function MinifyHelper()
		{
			parent::kHelper();

			$this->debugMode = $this->Application->isDebugMode(false);
			$this->infoFile = WRITEABLE . '/cache/compress_info.txt';

			if ( file_exists($this->infoFile) ) {
				$this->compressInfo = unserialize( file_get_contents($this->infoFile) );
			}
		}

		/**
		 * When used as non-block tag, then compress given files and return url to result
		 *
		 * @param Array $files
		 * @return string
		 */
		function CompressScriptTag($params)
		{
			// put to queue
			if (array_key_exists('to', $params)) {
				$files = $this->Application->GetVar($params['to'], '');
				$this->Application->SetVar($params['to'], $files . '|' . $params['files']);

				return '';
			}

			if (array_key_exists('from', $params)) {
				// get from queue
				$files = $this->Application->GetVar($params['from']);
			}
			else {
				// get from tag
				$files = $params['files'];
			}

			$files = $this->_getTemplatePaths( array_map('trim', explode('|', $files)) );
			$extension = pathinfo($files[0], PATHINFO_EXTENSION);

			$hash = ($this->debugMode ? 'd' : 'c') . '_' . $this->_getHash($files);
			$file_mask = 'cache/' . $hash . '_%s.' . $extension;

			$was_compressed = array_key_exists($hash, $this->compressInfo);

			if ($was_compressed) {
				$current_file = WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]);

				if (!file_exists($current_file)) {
					// when info exists, but file doesn't -> re-compress
					$was_compressed = false;
				}

				if ($this->debugMode) {
					// check if any of listed files was changed since compressed file was created (only, when in debug mode)
					foreach ($files as $file) {
						if (filemtime($file) > $this->compressInfo[$hash]) {
							$was_compressed = false;
							break;
						}
					}
				}
			}

			if (!$was_compressed) {
				$string = '';
				$path_length = strlen(FULL_PATH) + 1;

				foreach ($files as $file) {
					// add filename before for easier debugging
					if ($this->debugMode) {
						$string .= '/* === File: ' . substr($file, $path_length) . ' === */' . "\n";
						$string .= '/* ' . str_repeat('=', strlen(substr($file, $path_length)) + 14) . ' */' . "\n\n";
					}

					// add file content
					$string .= file_get_contents($file) . "\n\n";
				}

				// remove previous version of compressed file
				if (isset($current_file)) {
					if (file_exists($current_file)) {
						unlink($current_file);
					}
				}

				// replace templates base
				$templates_base = $this->Application->ProcessParsedTag('m', 'TemplatesBase', Array ());
				$templates_base = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', BASE_PATH . '/', $templates_base);
				$string = str_replace('@templates_base@', rtrim($templates_base, '/'), $string);

				// compress collected data
				$this->compressInfo[$hash] = adodb_mktime();

				if (!$this->debugMode) {
					// don't compress merged js/css file in debug mode to allow js/css debugging
					$this->compressString($string, $extension);
				}

				// save compressed file
				$fp = fopen(WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]), 'w');
				fwrite($fp, $string);
				fclose($fp);

				$this->_saveInfo();
			}

			// got compressed file -> use it
			return $this->Application->BaseURL( str_replace(DIRECTORY_SEPARATOR, '/', WRITEBALE_BASE)) . sprintf($file_mask, $this->compressInfo[$hash]);
		}

		/**
		 * Returns hash string based on given files
		 *
		 * @param Array $files
		 * @return int
		 */
		function _getHash($files)
		{
			$hash = $files;

			if ($this->Application->isAdmin) {
				array_unshift($hash, 'A:1');
			}
			else {
				array_unshift($hash, 'A:0;T:' . $this->Application->GetVar('m_theme'));
			}

			return crc32( implode('|', $hash) );
		}

		/**
		 * Saves file with compress information
		 *
		 */
		function _saveInfo()
		{
			$fp = fopen($this->infoFile, 'w');
			fwrite($fp, serialize($this->compressInfo));
			fclose($fp);
		}

		/**
		 * Deletes compression info file
		 *
		 * @todo also delete all listed there files
		 */
		function delete()
		{
			if (file_exists($this->infoFile)) {
				unlink($this->infoFile);
			}
		}

		/**
		 * Compress $string based on $extension
		 *
		 * @param string $string
		 * @param string $extension
		 */
		function compressString(&$string, $extension)
		{
			$tmp_file = tempnam('/tmp', 'to_compress_');
			$fp = fopen($tmp_file, 'w');
			fwrite($fp, $string);
			fclose($fp);

			$command = 'java -jar ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'yuicompressor-2.4.2.jar --type ' . $extension . ' --charset utf-8 ' . $tmp_file;
			$compressed_string = shell_exec($command);

			unlink($tmp_file);

			if (!is_null($compressed_string)) {
				$string = $compressed_string;
				return ;
			}

			// failed to compress using YUICompressor (maybe java not installed)

			if ($extension == 'js') {
				$minifier =& $this->Application->makeClass('JsMinifyHelper');
				/* @var $minifier JsMinifyHelper */

				$string = $minifier->minify($string);
			}
			elseif ($extension == 'css') {
				$minifier =& $this->Application->makeClass('CssMinifyHelper');
				/* @var $minifier CssMinifyHelper */

				$string = $minifier->minify($string);
			}
		}

		/**
		 * Get full paths on disk for each of given templates
		 *
		 * @param Array $templates
		 * @return Array
		 */
		function _getTemplatePaths($templates)
		{
			$ret = Array ();
			$reg_exp = '/^' . preg_quote($this->Application->BaseURL(), '/') . '(.*)/';

			foreach ($templates as $template) {
				if (!$template) {
					continue;
				}

				if (preg_match($reg_exp, $template, $regs)) {
					// full url (from current domain) to a file
					$path = FULL_PATH . '/' . $regs[1];
				}
				else {
					list ($path, $module_filename) = $this->Application->TemplatesCache->GetTemplatePaths($template);
					$path .= DIRECTORY_SEPARATOR . $module_filename;
				}

				$ret[] = $path;
			}

			return $ret;
		}
	}