<?php
/*
 * Copyright (c) 2012, webvariants GbR, http://www.webvariants.de
 *
 * This file is released under the terms of the MIT license. You can find the
 * complete text in the attached LICENSE file or online at:
 *
 * http://www.opensource.org/licenses/mit-license.php
 */

/**
 * @author Christoph Mewes, Christian Zozmann
 */
class WV5_Deployment {
	private static $filesChanged      = false;
	private static $debug             = false;
	private static $useTimeStamp      = false;
	private static $CSSIndices        = array();
	private static $JSIndices         = array();
	private static $JSCompressionMode = WV5_Deployer_JavaScript::COMPRESSION_NONE;

	/**
	 * Setzt den Debug modus
	 *
	 * @param boolean $debug
	 */
	public static function debug($debug) {
		self::$debug = (boolean) $debug;
	}

	/**
	 * Setzen ob in die virtuelle url ein timestamp integriert wird
	 *
	 * @param boolean $useTimeStamp
	 */
	public static function useTimeStamp($useTimeStamp) {
		self::$useTimeStamp = (boolean) $useTimeStamp;
	}

	/**
	 * Setzt die CSS Gruppen, die komprimiert werden sollen
	 *
	 * @param array $CSSIndices
	 */
	public static function setCSSIndices($CSSIndices) {
		self::$CSSIndices = sly_makeArray($CSSIndices);
	}

	/**
	 * Setzt die JS gruppen, die komprimiert werden sollen
	 *
	 * @param array $JSIndices
	 */
	public static function setJSIndices($JSIndices) {
		self::$JSIndices = sly_makeArray($JSIndices);
	}

	/**
	 * Setzt den Kompressionsmodus für Javascript Dateien
	 *
	 * @param string $mode
	 */
	public static function setJSCompressionMode($mode) {
		self::$JSCompressionMode = $mode;
	}

	public static function setFilesChanged() {
		self::$filesChanged = true;
	}

	public static function updateAssetCache(array $params = array()) {
		if (self::$filesChanged) {
			sly_Service_Factory::getAssetService()->validateCache();
		}

		return $params['subject'];
	}

	public static function bootcache($params) {
		if ($params['event'] === 'SLY_BOOTCACHE_CLASSES_FRONTEND') {
			sly_Util_BootCache::addClass('WV5_Deployer_Base');
			sly_Util_BootCache::addClass('WV5_Deployer_CSS');
			sly_Util_BootCache::addClass('WV5_Deployer_JavaScript');
		}
		sly_Util_BootCache::addClass(__CLASS__);
	}

	/**
	 * Cache leeren
	 *
	 * Diese Methode sorgt dafür, dass der Deployer alle Dateizeiten vergisst und
	 * beim nächsten Request den Cache neu aufbauen wird. Außerdem werden auch
	 * alle Cache-Dateien entfernt.
	 *
	 * @since 1.0
	 */
	public static function clearCache(array $params = array()) {
		$isSystem = sly_Core::getCurrentControllerName() === 'system';

		// do nothing if requested
		if ($isSystem) {
			$controller = sly_Core::getCurrentController();

			if (method_exists($controller, 'isCacheSelected') && !$controller->isCacheSelected('deployer')) {
				return $params['subject'];
			}
		}

		sly_Core::cache()->flush('wv5');
		$cacheFolder = new sly_Util_Directory(self::getCacheFolder());
		$cacheFolder->deleteFiles();

		return isset($params['subject']) ? $params['subject'] : true;
	}

	/**
	 * Callback zum Komprimieren von CSS Code
	 *
	 * @param  array $params
	 * @return string         CSS Code
	 */
	public static function compressCSS(array $params) {
		$cssCode = $params['subject'];
		return sly_Util_Lessphp::processString($cssCode);
	}

	/**
	 * Callback zum Komprimieren von JavaScript Code
	 *
	 * @param  array $params
	 * @return string         JavaScript Code
	 */
	public static function compressJS(array $params) {
		$jsCode = $params['subject'];
		return WV5_Util_JSMin::minify($jsCode);
	}

	/**
	 * SLY_SYSTEM_CACHES
	 *
	 * @param array $params  event parameters
	 */
	public static function systemCacheList(array $params) {
		$select     = $params['subject'];
		$selected   = $select->getValue();
		$selected[] = 'deployer';

		$select->addValue('deployer', 'komprimierte CSS/JavaScript-Dateien löschen');
		$select->setSelected($selected);

		return $select;
	}

	/**
	 * Callback zum komprimieren von CSS Dateien. Die Dateien werden Komprimiert
	 * und nach Gruppe und media Attribut zusammengefasst. Zusammengefasste Dateien
	 * bekommen einen neuen virtuellen Dateinamen.
	 *
	 * @param  array $params  nach dem Schema gruppe => media => datei => srcAttribut
	 * @return array          Enthält die neuen virtuellen Dateinamen
	 */
	public static function compressCSSFiles($params) {
		$files  = $params['subject'];
		$result = array();

		foreach ($files as $group => $medias) {
			if (!in_array($group, self::$CSSIndices)) {
				$result[$group] = $medias;
			}
			else {
				foreach ($medias as $media => $filearray) {
					$result[$group][$media][]['src'] = self::quickDeployCSS($filearray, self::$useTimeStamp);
				}
			}
		}

		return $result;
	}

	/**
	 * Callback zum komprimieren von Javascript Dateien. Die Dateien werden Komprimiert
	 * und nach der Gruppe zusammengefasst. Zusammengefasste Dateien
	 * bekommen einen neuen virtuellen Dateinamen.
	 *
	 * @param  array $params
	 * @return array          Enthält die neuen virtuellen Dateinamen
	 */
	public static function compressJSFiles($params) {
		$files = $params['subject'];

		foreach ($files as $group => $filearray) {
			if (!in_array($group, self::$JSIndices)) continue;
			try {
				$result = array(self::quickDeployJS($filearray, self::$JSCompressionMode, self::$useTimeStamp));
			} catch (Exception $e) {
				$result = $filearray;
			}
			$files[$group] = $result;
		}

		return $files;
	}

	/**
	 * CSS-Dateien zusammenfügen und komprimieren
	 *
	 * Nimmt eine Liste von CSS-Dateien entgegen und komprimiert diese.
	 *
	 * Die Dateien werden alle eingelesen, komprimiert und in einer gebündelten
	 * Datei gespeichert. Die Methode merkt sich die mtime der Dateien, um nicht
	 * bei jedem Aufruf die Dateien erneut komprimieren zu müssen. Das Speichern
	 * dieser Information erfolgt in der Registry.
	 *
	 * @since  1.0
	 * @param  array   $files          eine Liste von CSS-Dateien
	 * @param  boolean $withTimestamp  wenn true, wird an den virtuellen Dateinamen der Timestamp der letzten mtime angehängt
	 * @return string                  der (virtuelle) Dateiname der komprimierten Datei
	 */
	protected static function quickDeployCSS($files, $withTimestamp = true) {
		$files = sly_makeArray($files);
		if (empty($files)) return false;

		foreach ($files as $key => $file) {
			$files[$key] = $file['src'];
		}

		$deployer = new WV5_Deployer_CSS($files);
		return self::deploy($deployer, $withTimestamp);
	}

	/**
	 * JavaScript-Dateien zusammenfügen und komprimieren
	 *
	 * Nimmt eine Liste von JS-Dateien entgegen und komprimiert diese.
	 *
	 * Die Dateien werden alle eingelesen, komprimiert und in einer gebündelten
	 * Datei gespeichert. Die Methode merkt sich die mtime der Dateien, um nicht
	 * bei jedem Aufruf die Dateien erneut komprimieren zu müssen. Das Speichern
	 * dieser Information erfolgt in der Registry.
	 *
	 * Das Komprimieren erfolgt mit einer leicht abgeänderten (semantisch
	 * identischen) Version von JSMin oder dem Google Closure Compiler.
	 *
	 * @since  1.0
	 * @param  array   $files          eine Liste von JS-Dateien
	 * @param  string  $compression    'none' für keine, 'jsmin' oder 'google' (siehe WV5_Deployer_JavaScript)
	 * @param  boolean $withTimestamp  wenn true, wird an den virtuellen Dateinamen der Timestamp der letzten mtime angehängt
	 * @return string                  der (virtuelle) Dateiname der komprimierten Datei
	 */
	protected static function quickDeployJS($files, $compression = WV5_Deployer_JavaScript::COMPRESSION_NONE, $withTimestamp = true) {
		$files = sly_makeArray($files);
		if (empty($files)) return false;

		$deployer = new WV5_Deployer_JavaScript($files);
		$deployer->setCompression($compression);
		return self::deploy($deployer, $withTimestamp);
	}

	/**
	 * Deployment-Hilfsfunktion
	 *
	 * Da die Verfahren, nachdem ein Deployer initialisiert wurde, für CSS und JS
	 * identisch sind, wurde die Implementierung in diese Methode ausgelagert.
	 * Sie wird entsprechend create() aufrufen und dann einen virtuellen
	 * Dateinamen aus dem Hash und der mtime (hex) erstellen und zurückgeben.
	 *
	 * Im Frontend sollten dann i.d.R. Requests auf diese virtuellen Dateien via
	 * mod_rewrite auf die echte Datei umgeleitet werden.
	 *
	 * @since  1.0
	 * @param  WV5_Deployer_Base $deployer       der zu verwendende Deployer
	 * @param  boolean           $withTimestamp  wenn true, wird an den virtuellen Dateinamen der Timestamp der letzten mtime angehängt
	 * @return string                            der (virtuelle) Dateiname der komprimierten Datei
	 */
	protected static function deploy(WV5_Deployer_Base $deployer, $withTimestamp) {
		// Let's go!
		$type   = $deployer->getTypeString();
		$result = $deployer->create();
		if ($result === false) return '';

		extract($result);

		// Ziel-Dateinamen erzeugen

		$timestamp = $withTimestamp ? '?t='.$mtime_hex : '';
		$virtual   = 'assets/'.($type === 'less' ? 'css' : $type).'/deployer.'.$hash.'.min.'.$type.$timestamp;

		return $virtual;
	}

	/**
	 * Cache-Verzeichnis ermitteln
	 *
	 * Je nach zugrunde liegendem System werden die generierten Dateien in
	 * anderen Verzeichnissen abgelegt. Hierbei muss beachtet werden, dass diese
	 * Verzeichnisse mit den Rewrite-Regeln aus der .htaccess übereinstimmen
	 * müssen!
	 *
	 * Sollte das Verzeichnis nicht existieren, wird es angelegt.
	 *
	 * @since  1.0
	 * @throws Exception  falls das Cache-Verzeichnis nicht angelegt werden konnte
	 * @return string     das Cache-Verzeichnis (ohne Slash am Ende)
	 */
	public static function getCacheFolder() {
		return sly_Util_AddOn::publicDirectory('webvariants/deployer');
	}

	/**
	 * Dateiname auf Kennzeichen für Komprimierung untersuchen
	 *
	 * Wenn eine Datei auf eine der folgenden Endungen hört, wird sie als bereits
	 * komprimiert angesehen:
	 *
	 * - .compressed.XY
	 * - .minified.XY
	 * - .minimized.XY
	 * - .min.XY
	 *
	 * @since  1.0
	 * @param  string $filename  der zu überprüfende Dateiname
	 * @param  string $type      'css' oder 'js'
	 * @return boolean           true, wenn die Datei vermutlich bereits komprimiert ist
	 */
	public static function isMinified($filename, $type) {
		return
			sly_Util_String::endsWith($filename, '.compressed.'.$type) ||
			sly_Util_String::endsWith($filename, '.minimized.'.$type) ||
			sly_Util_String::endsWith($filename, '.minified.'.$type) ||
			sly_Util_String::endsWith($filename, '.min.'.$type);
	}
}
