<?php
/**
 *---------------------------------------------------------------------------------------
 * @package       VirtuePlanet Framework for Joomla!
 *---------------------------------------------------------------------------------------
 * @copyright     Copyright (C) 2012-2021 VirtuePlanet Services LLP. All rights reserved.
 * @license       GNU General Public License version 2 or later; see LICENSE.txt
 * @authors       Abhishek Das
 * @email         info@virtueplanet.com
 * @link          https://www.virtueplanet.com
 *---------------------------------------------------------------------------------------
 */
defined('_JEXEC') or die;

jimport('joomla.filesystem.folder');
jimport('joomla.filesystem.file');

class VPFrameworkOptimizer extends JObject
{
	protected $app;
	protected $params;
	protected $parser;
	protected $css_parser;
	protected $lifeTime;
	protected $delivery_base_url;
	protected $delivery_base_path;
	protected $original_buffer;
	
	protected $modrewrite_enabled = null;
	protected $primary_hash       = null;
	protected $elements           = array();
	protected $pcre_jit           = null;
	
	public static $instance       = null;
	
	protected static $cdn_enabled = null;
	protected static $cache_files = array();
	
	
	public function __construct($buffer = null, $params = null)
	{
		$template  = plgSystemVPFrameworkHelper::getTemplate();
		$this->app = JFactory::getApplication();
		
		if(!$buffer)
		{
			$buffer = $this->app->getBody();
		}
		
		if($params && is_object($params))
		{
			$this->params = $params;
		}
		else
		{
			$this->params = $template->params;
		}
		
		// Do not combine and cache frontend edit
		if($template->_isFrontendEdit())
		{
			$this->params->set('optimizer_combine_css', 0);
			$this->params->set('optimizer_combine_js', 0);
		}
		
		if($this->params->get('optimizer_combine_css') || $this->params->get('optimizer_combine_js') || $this->params->get('optimizer_compress_html'))
		{
			if(function_exists('ini_set'))
			{
				ini_set('pcre.backtrack_limit', 1000000);
				ini_set('pcre.recursion_limit', 100000);
				
				if(version_compare(PHP_VERSION, '7', '>='))
				{
					$this->pcre_jit = ini_get('pcre.jit');
					ini_set('pcre.jit', 0);
				}
			}
		}
		
		$this->original_buffer = $buffer;
		$this->lifeTime        = (int) $this->params->get('optimizer_cache_lifetime', 24) * 60;
		$this->parser          = new VPFrameworkOptimizerParser($this->original_buffer, $this->params);
		$this->css_parser      = new VPFrameworkOptimizerCssparser($this->params, false);
		
		$this->delivery_base_url  = JUri::root(true) . '/media/vpframework/optimized';
		$this->delivery_base_path = JPATH_SITE . '/media/vpframework/optimized';
	}
	
	public static function getInstance($buffer = null, $params = null)
	{
		if(self::$instance === null)
		{
			spl_autoload_register(array('VPFrameworkOptimizer', '_loader'));
			
			self::$instance = new VPFrameworkOptimizer($buffer, $params);
		}
		
		return self::$instance;
	}
	
	public static function getDeliveryBase($type, $noCdn = false, $params = null)
	{
		$directory = '/media/vpframework/optimized';
		
		if(strtoupper($type) == 'PATH')
		{
			return JPATH_SITE . $directory;
		}
		else
		{
			if(!$params)
			{
				$params = plgSystemVPFrameworkHelper::getTemplate()->params;
			}
			
			if(!$noCdn && self::cdnEnabled($params))
			{
				$domains = self::getCdnDomains($params);
				
				return self::chooseCdnDomain($directory, $domains);
			}
			else
			{
				return JUri::root(true) . $directory;
			}
		}
	}
	
	public static function setCdn($path, $params = null)
	{
		if(!$params)
		{
			$params = plgSystemVPFrameworkHelper::getTemplate()->params;
		}
		
		if(!self::cdnEnabled($params))
		{
			return $path;
		}
		
		$domains = self::getCdnDomains($params);
		
		return self::chooseCdnDomain($path, $domains);
	}
	
	public static function cdnEnabled($params = null)
	{
		if(self::$cdn_enabled === null)
		{
			if(!$params)
			{
				$params = plgSystemVPFrameworkHelper::getTemplate()->params;
			}
			
			self::$cdn_enabled = false;
			
			if($params->get('optimizer_use_cdn', 0))
			{
				$domains = self::getCdnDomains($params);

				if(!empty($domains))
				{
					self::$cdn_enabled = true;
				}
			}
		}
		
		return self::$cdn_enabled;
	}
	
	public static function getCdnDomains($params = null)
	{
		static $domains = null;
		
		if($domains === null)
		{
			if(!$params)
			{
				$params = plgSystemVPFrameworkHelper::getTemplate()->params;
			}
			
			$domains = array();
			
			$scheme   = $params->get('optimizer_cdn_scheme', '0');
			$domain_1 = $params->get('optimizer_cdn_domain_1', '');
			$domain_2 = $params->get('optimizer_cdn_domain_2', '');
			$domain_3 = $params->get('optimizer_cdn_domain_3', '');
			
			if(!empty($domain_1))
			{
				$domains[] = self::prepareCdn($scheme, $domain_1);
			}
			
			if(!empty($domain_2))
			{
				$domains[] = self::prepareCdn($scheme, $domain_2);
			}
			
			if(!empty($domain_3))
			{
				$domains[] = self::prepareCdn($scheme, $domain_3);
			}
		}
		
		return $domains;
	}
	
	
	public static function optimize($buffer = null, $params = null)
	{
		if (version_compare(PHP_VERSION, '5.3.0', '<'))
		{
			throw new Exception('PHP Version less than 5.3.0. Exiting plugin...');
		}

		$pcre_version = preg_replace('#(^\d++\.\d++).++$#', '$1', PCRE_VERSION);

		if (version_compare($pcre_version, '7.2', '<'))
		{
			throw new Exception('PCRE Version less than 7.2. Exiting plugin...');
		}
		
		return self::getInstance($buffer, $params)->process();
	}
	
	protected function process()
	{
		try
		{
			// If not valid html
			if(!$this->parser->getHead(true))
			{
				return $this->original_buffer;
			}
			
			// Start parsing
			$this->parser->parse();
			
			// Get buffer after after replacements
			$buffer = $this->parser->getBuffer();
			
			// Get the list of assets to be processed
			$scripts       = $this->parser->get('scripts');
			$inlineScripts = $this->parser->get('inlineScripts');
			$styleSheets   = $this->parser->get('styleSheets');
			$inlineStyles  = $this->parser->get('inlineStyle');
			
			// Build combined cached contents
			$this->buildCombinedContents('js', $scripts);
			$this->buildCombinedContents('js_inline', $inlineScripts);
			$this->buildCombinedContents('css', $styleSheets);
			$this->buildCombinedContents('css_inline', $inlineStyles);
			
			// Get HTML elements for the assets
			$htmlElements = $this->getHtmlElements();
			
			$placeholders = array(
				'<VPOPTIMIZER_PLACEHOLDER_FOR_CSS/>',
				'<VPOPTIMIZER_PLACEHOLDER_FOR_CSS_INLINE/>'
			);
			
			$elements = array(
				$htmlElements['css'],
				$htmlElements['css_inline']
			);
			
			if(!$this->params->get('optimizer_bottom_js', 1))
			{
				$placeholders[] = '<VPOPTIMIZER_PLACEHOLDER_FOR_JS/>';
				$placeholders[] = '<VPOPTIMIZER_PLACEHOLDER_FOR_JS_INLINE/>';
				
				$elements[] = $htmlElements['js'];
				$elements[] = $htmlElements['js_inline'];
			}
			else
			{
				$doc     = JFactory::getDocument();
				$tab     = $doc->_getTab();
				$lineEnd = $doc->_getLineEnd();
				
				$bottomBody  = $htmlElements['js'] . $lineEnd;
				$bottomBody .= $htmlElements['js_inline'] ? $htmlElements['js_inline'] . $lineEnd : '';
				$bottomBody .= '</body>';
				
				$buffer = str_replace('</body>', $bottomBody, $buffer);
			}
			
			if($this->params->get('optimizer_clean_cache', 0) && !empty($htmlElements['gc']))
			{
				$state         = VPFrameworkState::getInstance();
				$date          = JFactory::getDate();
				$last_run_date = $state->get('optimizer.cacheclean.date', '0000-00-00 00:00:00');
				$last_run_date = $last_run_date ? JFactory::getDate($last_run_date) : null;
				$interval      = (int) $this->params->get('optimizer_cache_lifetime', 24);
				$interval      = ($interval < 1) ? 1 : $interval;
				$run_cron      = false;
				
				if(!$last_run_date)
				{
					$run_cron = true;
				}
				else
				{
					$interval = $interval * 60 * 60; // Convert hours to seconds
					$last_run_date->add(DateInterval::createFromDateString($interval . ' seconds'));
					
					if($last_run_date <= $date)
					{
						$run_cron = true;
					}
				}
				
				if($run_cron)
				{
					$buffer = str_replace('</body>', $htmlElements['gc'] . '</body>', $buffer);
					
					// Save the last run time
					$state->set('optimizer.cacheclean.date', $date->toSql());
				}
			}
			
			$buffer = str_replace($placeholders, $elements, $buffer);
			
			// Minify HTML
			if($this->params->get('optimizer_compress_html', 0))
			{
				$buffer = VPFrameworkOptimizerBuilder::minifyContent('html', $buffer, 'Minify HTML');
			}
		}
		catch(Exception $e)
		{
			$this->app->enqueueMessage('VP Framework Optimizer failed due to the following error. ' . $e->getMessage(), 'error');
			//TODO:: Log error
			
			$buffer = $this->original_buffer;
		}
		
		if($this->pcre_jit !== null && function_exists('ini_set'))
		{
			ini_set('pcre.jit', $this->pcre_jit);
		}
		
		return $buffer;
	}
	
	public function buildCombinedContents($type, $sets)
	{
		if(!empty($sets))
		{
			foreach($sets as $set)
			{
				if(empty($set))
				{
					continue;
				}
				
				$cacheContents = true;
				$cachePrefix = $set;
				
				if(($type == 'js_inline' || $type == 'css_inline') && isset($set[0]) && $set[0]['exclude'])
				{
					$cacheContents = false;
				}
				elseif($type == 'js' || $type == 'css')
				{
					$cachePrefix = array_keys($set);
					
					// Sort to avoid duplicate caching
					sort($cachePrefix);
				}

				$cache_id = md5(serialize($cachePrefix) . $this->getHash());

				$content  = $this->getCombinedFiles($type, $set, $cache_id, $cacheContents);
				
				$this->addElement($type, $cache_id, $content);
			}
		}
	}
	
	protected function addElement($type, $cache_id, $content)
	{
		static $index = 0;
		
		if(empty($this->elements))
		{
			$this->elements['js']           = array();
			$this->elements['js_excluded']  = array();
			$this->elements['js_inline']    = array();
			$this->elements['css']          = array();
			$this->elements['css_excluded'] = array();
			$this->elements['css_inline']   = array();
		}
		
		$type    = strtolower($type);
		$element = new stdClass;
		
		$element->name = null;
		$element->html = null;
		$element->content = null;

		if(is_array($content) && !empty($content['element']))
		{
			$element->name = $cache_id;
			$element->html = $content['element'];
			
			if($type == 'js_inline' || $type == 'css_inline')
			{
				$this->elements[$type][$index] = $element;
			}
			else
			{
				$this->elements[$type . '_excluded'][$index] = $element;
			}
		}
		else
		{
			if($type == 'js_inline' || $type == 'css_inline')
			{
				$elementKey = str_replace('_inline', '', $type);
				$lastKey    = VPFrameworkUtility::getLast($this->elements[$elementKey], true);
				
				if(!empty($this->elements[$elementKey][$lastKey]))
				{
					$oldName    = $this->elements[$elementKey][$lastKey]->name;
					$oldContent = $this->elements[$elementKey][$lastKey]->content;
					
					$element->name    = md5($oldName . $cache_id);
					$element->content = $this->mergeContents($oldContent, $content, $element->name);
				}
				else
				{
					$element->name    = $cache_id;
					$element->content = $content;
				}
				
				$this->elements[$elementKey][$lastKey] = $element;
			}
			elseif($type == 'js' || $type == 'css')
			{
				$element->name    = $cache_id;
				$element->content = $content;
				
				$this->elements[$type][$index] = $element;
			}
		}
		
		$index++;
	}
	
	protected function getHtmlElements()
	{
		$js         = isset($this->elements['js']) ? $this->elements['js'] : array();
		$css        = isset($this->elements['css']) ? $this->elements['css'] : array();
		$js_inline  = isset($this->elements['js_inline']) ? $this->elements['js_inline'] : array();
		$css_inline = isset($this->elements['css_inline']) ? $this->elements['css_inline'] : array();
		
		if(!empty($this->elements['js_excluded']))
		{
			$js = $js + $this->elements['js_excluded'];
			
			ksort($js);
		}
		
		if(!empty($this->elements['css_excluded']))
		{
			$css = $css + $this->elements['css_excluded'];
			
			ksort($css);
		}
		
		$doc      = JFactory::getDocument();
		$tab      = $doc->_getTab();
		$lineEnd  = $doc->_getLineEnd();
		$gzip_js  = $this->params->get('optimizer_gzip_js', 1);
		$gzip_css = $this->params->get('optimizer_gzip_css', 1);
		
		$js_html         = array();
		$css_html        = array();
		$js_inline_html  = array();
		$css_inline_html = array();
		
		foreach($js as $object)
		{
			if(!empty($object->html))
			{
				$js_html[] = $object->html;
			}
			else
			{
				$url = $this->buildURL('js', $object->name, $gzip_js);
				
				$html  = $tab . $tab . '<script';
				$html .= ' src="' . $url . '"';
				$html .= ' type="text/javascript"';
				
				if($this->params->get('optimizer_async_js', 1))
				{
					$html .= ' async';
				}
				
				if($this->params->get('optimizer_defer_js', 0))
				{
					$html .= ' defer';
				}
				
				$html .= '>';
				$html .= '</script>';
				
				$js_html[] = $html;
			}
		}
		
		foreach($css as $object)
		{
			if(!empty($object->html))
			{
				$css_html[] = $object->html;
			}
			else
			{
				$url = $this->buildURL('css', $object->name, $gzip_css);
				
				$html  = '<link rel="stylesheet"';
				$html .= ' href="' . $url . '"';
				$html .= ' type="text/css"';
				$html .= ' />';
				
				$css_html[] = $html;
			}
		}
		
		foreach($js_inline as $object)
		{
			if(!empty($object->html))
			{
				$js_inline_html[] = $object->html;
			}
			elseif(!empty($object->content))
			{
				$html  = '<script type="text/javascript">' . $lineEnd;
				$html .= $object->content . $lineEnd;
				$html .= '</script>';
				
				$js_inline_html[] = $html;
			}
		}
		
		foreach($css_inline as $object)
		{
			if(!empty($object->html))
			{
				$css_inline_html[] = $object->html;
			}
			elseif(!empty($object->content))
			{
				$html  = $tab . $tab . '<style type="text/css">' . $lineEnd;
				$html .= $object->content . $lineEnd;
				$html .= '</style>';
				
				$css_inline_html[] = $html;
			}
		}
		
		$js_html         = !empty($js_html) ? implode($lineEnd . $tab, $js_html) : '';
		$css_html        = !empty($css_html) ? implode($lineEnd . $tab, $css_html) : '';
		$js_inline_html  = !empty($js_inline_html) ? implode($lineEnd, $js_inline_html) : '';
		$css_inline_html = !empty($css_inline_html) ? implode($lineEnd, $css_inline_html) : '';
		
		$uri     = VPFrameworkUrl::getInstance();
		$host    = $uri->toString(array('scheme', 'user', 'pass', 'host', 'port'));
		$gc_url  = $this->buildURL('gif', 'gc', false, true, true, true);
		$gc_url  = self::url_encode($host . $gc_url);
		$gc_html = $tab . $tab . '<img src="' . $this->buildURL('gif', $gc_url, false, true, true) . '" width="1" height="1" alt="gc" class="sr-only" />'. $lineEnd;
		
		return array('js' => $js_html, 'css' => $css_html, 'js_inline' => $js_inline_html, 'css_inline' => $css_inline_html, 'gc' => $gc_html);
	}
	
	protected function mergeContents($oldContent, $newContent, $cache_id)
	{
		$cache     = $this->getCache('callback', $this->lifeTime);
		$callback  = array($this, 'merge');
		$arguments = array($oldContent, $newContent);
		
		return $cache->get($callback, $arguments, $cache_id);
	}
	
	public function merge($oldContent, $newContent)
	{
		return $oldContent . $newContent;
	}
	
	protected function getCombinedFiles($type, $links, $cache_id, $cacheContent = true)
	{
		$cache     = $this->getCache('callback', $this->lifeTime);
		$hash      = $this->getHash();
		$builder   = new VPFrameworkOptimizerBuilder($this->params, $this->parser, $this->css_parser, $cache, $hash);
		
		if($cacheContent)
		{
			$callback  = array(&$builder, 'getContents');
			$arguments = array($type, $links);

			$result = $this->loadCache($callback, $arguments, $cache_id);
		}
		else
		{
			$result = $builder->getContents($type, $links);
		}

		if($result === false)
		{
			throw new Exception(__METHOD__ . '() failed. Error creating cache file.');
		}
			
		return $result;
	}
	
	public function isModRewriteEnabled()
	{
		$use_mod_rewrite = $this->params->get('optimizer_use_mod_rewrite', '');
		
		if (is_numeric($use_mod_rewrite))
		{
			$this->modrewrite_enabled = (int) $use_mod_rewrite;
		}
		
		if($this->modrewrite_enabled === null)
		{
			$uri           = VPFrameworkUrl::getInstance();
			$http          = VPFrameworkHttp::getInstance();
			$host          = $uri->toString(array('scheme', 'user', 'pass', 'host', 'port'));
			$delivery_base = $this->getDeliveryBase('URL', true);
			$url           = $host . $delivery_base . '/normal/' . $this->lifeTime . '/test.css';
			$cache_id      = md5($this->getHash() . 'modrewriteenabled');
			$http->strict  = true;
			
			if(!$http->hasAdapter())
			{
				$this->modrewrite_enabled = false;
			}
			else
			{
				$this->enableHTACCESS();
				
				$cache     = $this->getCache('callback', $this->lifeTime);
				$callback  = array(&$http, 'isUrlAccessible');
				$arguments = array($url);
				
				$this->modrewrite_enabled = $cache->get($callback, $arguments, $cache_id);
			}
			
			if(!$this->modrewrite_enabled)
			{
				$this->disableHTACCESS();
			}
		}
		
		return $this->modrewrite_enabled;
	}
	
	protected function buildURL($type, $key, $gzip = false, $noCdn = false, $noCache = false, $noSEF = false)
	{
		$modrewrite_enabled = $this->isModRewriteEnabled();
		$delivery_base      = $this->getDeliveryBase('URL', $noCdn);
		
		if(!$noSEF && $modrewrite_enabled)
		{
			$url = array();
			
			$url[] = $delivery_base;
			$url[] = (!$gzip ? 'n' : 'gz');
			$url[] = $this->lifeTime;
			$url[] = strval($key) . '.' . strval($type);
			
			$url = implode('/', $url);
			
			if($noCache)
			{
				$url .= '?_' . time();
			}
		}
		else
		{
			$query = array();
			
			$query['type'] = strval($type);
			$query['gz']   = intval($gzip);
			$query['lt']   = $this->lifeTime;
			$query['key']  = strval($key);
			$query['_']    = time();
			
			$url = $delivery_base . '/asset.php?' . http_build_query($query);
		}
		
		return $url;
	}
	
	protected function loadCache($callback, $arguments, $cache_id)
	{
		$cache = $this->getCache('callback', $this->lifeTime);
		
		return $cache->get($callback, $arguments, $cache_id);
	}

	protected function getCache($type = 'callback', $lifetime = 86400)
	{
		return self::getJoomlaCache($type, $lifetime);
	}
	
	public static function getJoomlaCache($type = 'callback', $lifetime = 86400)
	{
		$options = array(
			'defaultgroup' => 'vp_framework_assets',
			'checkTime'    => true,
			'application'  => 'site',
			'language'     => 'en-GB',
			'cachebase'    => JPATH_SITE . '/cache',
			'storage'      => 'file'
		);

		$cache = JCache::getInstance($type, $options);

		$cache->setCaching(true);
		$cache->setLifeTime($lifetime);

		return $cache;
	}
	
	public static function gc($lifetime = 86400)
	{
		$assetsResult = self::getJoomlaCache('output', $lifetime)->clean('vp_framework_assets');
		
		$result = JFactory::getCache('')->gc();
		
		vpdump($assetsResult);
		vpdump($result);
		exit;
	}
	
	public static function getOutput($cache_id, $lifetime)
	{
		$cache = self::getJoomlaCache('output', $lifetime);
		$output = $cache->get($cache_id);
		
		if($output === false || !isset($output['result']))
		{
			return false;
		}
		
		return $output['result'];
	}
	
	public static function getCacheFilename($id)
	{
		if(!isset(self::$cache_files[$id]))
		{
			$config = JFactory::getConfig();
			$hash   = md5($config->get('secret'));
			$prefix = JCache::getPlatformPrefix();
			$name   = md5('site-' . $id . '-en-GB');
			$group  = 'vp_framework_assets';
			
			self::$cache_files[$id] = $prefix . $hash . '-cache-' . $group . '-' . $name;
		}
		
		return self::$cache_files[$id];
	}
	
	public static function cacheFileExists($id)
	{
		$filename = self::getCacheFilename($id);
		$file     = JPATH_SITE . '/cache/vp_framework_assets/' . $filename . '.php';
		
		if(file_exists($file))
		{
			return $file;
		}
		
		return false;
	}
	
	public static function getCacheModifiedTime($id)
	{
		$file = self::cacheFileExists($id);
		
		if($file !== false)
		{
			return filemtime($file);
		}
		
		return time();
	}
	
	/**
	* Method to autoload all VPFrameworkOptimizer support classes
	* 
	* @param string $class Name of the helper class
	* 
	* @return boolean True is sucess or fals if failure
	*/
	protected static function _loader($class) 
	{
		// If already loaded return true
		if(class_exists($class, false))
		{
			return true;
		}
		
		// If class does not have VPFrameworkOptimizer prefix then it is not our helper.
		$prefix = 'VPFrameworkOptimizer';
		
		if(strlen($class) <= strlen($prefix) || strpos(strtolower($class), strtolower($prefix)) !== 0)
		{
			return;
		}
		
		// Path of helpers directory
		$path = VPF_PLUGINPATH . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'optimizer';
		
		// Retrieve the filename from the class
		$filename = strtolower(substr($class, strlen($prefix))) . '.php';
		
		// Build full file path
		$file = $path . DIRECTORY_SEPARATOR . $filename;
		
		if(file_exists($file) && !class_exists($class)) 
		{
			// echo 'Trying to load ', $class, ' via ', __METHOD__, "()\n";
			// Lets try to load the helper file if class no exists			
			require_once($file);
			
			// If the class still does exists return false
			if (!class_exists($class))
			{
				return false;
			} 
		}
		
		// Class loaded. Return true.
		return true;
	}
	
	protected function getHash()
	{
		if($this->primary_hash === null)
		{
			$params   = $this->params->toArray();
			$host     = VPFrameworkUrl::getInstance()->getHost();
			$template = plgSystemVPFrameworkHelper::getTemplate();
			$version  = !empty($template->xml->version) ? $template->xml->version : 'UNKNOWN';
			
			$this->primary_hash = serialize($params) . $host . $version;
			
			if($this->params->get('optimizer_browser_specific', 0))
			{
				$browser = VPFrameworkBrowser::getInstance();
				
				$this->primary_hash .= '.useragent:' . $browser->getUserAgent();
			}
		}
		
		return $this->primary_hash;
	}

	protected function enableHTACCESS()
	{
		$delivery_base = $this->getDeliveryBase('PATH');
		$file = $delivery_base . '/.htaccess';
		
		if(!JFile::exists($file))
		{
			$doc     = JFactory::getDocument();
			$lineEnd = $doc->_getLineEnd();
			
			$buffer  = '# VP Framework' . $lineEnd;
			$buffer .= '# Optimized CSS and JavaScript Delivery' . $lineEnd;
			$buffer .= '####' . $lineEnd;
			$buffer .= '<IfModule mod_rewrite.c>' . $lineEnd;
			$buffer .= 'RewriteEngine On' . $lineEnd;
			$buffer .= 'RewriteRule ^([^/]*)/([^/]*)/([^/]*)\.([^/]*)$ asset.php?type=$4&gz=$1&key=$3&lt=$2 [L]' . $lineEnd;
			$buffer .= '</IfModule>' . $lineEnd;
			$buffer .= '###';
			
			$return = JFile::write($file, $buffer);
			
			if($return === false)
			{
				// File creation failed
				return false;
			}
		}
		
		return true;
	}
	
	protected function disableHTACCESS()
	{
		$delivery_base = $this->getDeliveryBase('PATH');
		$file = $delivery_base . '/.htaccess';
		
		if(!JFile::exists($file))
		{
			$return = JFile::delete($file);
			
			if($return === false)
			{
				$this->throwError('Your site is not in modRewrite. Please delete this file manually ' . $file);
				return false;
			}
		}
		
		return true;
	}
	
	private static function prepareCdn($scheme, $domain)
	{
		switch($scheme)
		{
			case '1':
				$scheme = 'http:';
				break;
			case '2':
				$scheme = 'https:';
				break;
			case '0':
			default:
				$scheme = '';
				break;
		}
		
		$domain = $scheme . '//' . preg_replace('#^(?:https?:)?//|/$#i', '', $domain);;
		$domain = rtrim($domain, '/');

		return $domain;
	}
	
	private static function chooseCdnDomain($path, $domains)
	{
		if(empty($domains))
		{
			return $path;
		}
		
		static $count = 0;
		static $last  = null;
		static $list  = array();
		
		reset($domains);
		$path = $path ? preg_replace('#\?.*$#', '', $path) : '';
		
		if($count === 0)
		{
			$count = count($domains);
		}
		
		if($count == 1)
		{
			return $domains[0] . $path;
		}
		
		if($path && isset($list[$path]))
		{
			$domain_key = $list[$path];
			
			if(isset($domains[$domain_key]))
			{
				return $domains[$domain_key] . $path;
			}
		}
		
		if($last === null)
		{
			$last = -1;
		}
		
		$last++;

		if(!isset($domains[$last]))
		{
			$last = 0;
		}
		
		if($path)
		{
			$list[$path] = $last;
		}
		
		return $domains[$last] . $path;
	}
	
	
	private function throwError($message, $type = 'error', $adminOnly = false)
	{
		if($adminOnly && JFactory::getUser()->get('guest'))
		{
			return;
		}
		
		$message = JText::_($message);

		// Check if message is not already in queue
		$messagequeue = $this->app->getMessageQueue();
		
		foreach($messagequeue as $message)
		{
			if ($message['message'] == $text)
			{
				return;
			}
		}

		$this->app->enqueueMessage($message, $type);
	}
	
	public static function url_encode($string)
	{
		return strtr(base64_encode($string), '+/=', '._-');
	}
	
	public static function url_decode($string)
	{
		return base64_decode(strtr($string, '._-', '+/='));
	}
}