<?php

use Intellex\Filesystem\Dir;
use Intellex\Filesystem\File;
use Intellex\Manicure\Engine;
use Intellex\Manicure\Source\LocalSource;

define('THEME_LOCATION', APP . 'webroot' . DS . 'theme' . DS);
define('THEME_DEFAULT_LOCATION', VECTORCMS_ROOT . 'webroot' . DS . 'theme' . DS);

class Theme extends AppModel {

	/** @var string The name of the table to use. */
	public $useTable = 'theme';

	/** @const string The name of the CSS file which overrides the default color scheme.. */
	const CSS_FILENAME = 'color-scheme.css';

	/**
	 * Get the currently active theme.
	 *
	 * @return array The data about the current theme.
	 */
	public static function getActive() {

		// Default values
		$theme = [
			'primary_color' => '#3FA04D',
			'secondary_color' => '#5cb85c'
		];

		// Load from the css
		$themeStyle = new File(THEME_LOCATION . static::CSS_FILENAME);
		if ($themeStyle->isReadable()) {

			// Parse the info
			$content = $themeStyle->read();
			preg_match_all('~Theme\[(?P<var>\w+)\]\s*=\s*(?P<value>.+?)\s*$~ usim', $content, $values);
			for ($i = 0; $i < sizeof($values['var']); $i++) {
				if (key_exists($values['var'][$i], $theme)) {
					$theme[$values['var'][$i]] = $values['value'][$i];
				}
			}
		}

		// Load all uploads
		$uploads = static::getListOfUploads();
		foreach ($uploads as $upload => $info) {
			$filename = static::getFilename($upload);
			$theme['Uploads'][$upload] = is_readable(THEME_LOCATION . $filename)
				? THEME_LOCATION . $filename
				: THEME_DEFAULT_LOCATION . $filename;
		}

		return $theme;
	}

	/**
	 * Get the list of all uploads for the theme.
	 *
	 * @return array The list of all uploads for the theme.
	 */
	public static function getListOfUploads() {
		return [
			'Favicon' => [
				'label' => __('Website favicon') ],
			'Login' => [
				'label' => __('Login image') ],
			'Decoration' => [
				'label' => __('Decoration') ]
		];
	}

	/**
	 * Apply the theme.
	 *
	 * @param mixed $data The data to apply.
	 *
	 * @return bool True on success, false on error.
	 */
	public static function apply($data) {

		// Extract uploads
		$uploads = $data['Uploads'];
		unset($data['Uploads']);

		// Apply uploads
		foreach ($uploads as $upload => $token) {
			if (!empty($token)) {
				static::saveUpload($upload, $token);

				// Favicon
				if ($upload === 'Favicon') {
					static::generateFavicons();
				}
			}
		}

		// Apply CSS
		static::populateStyleTemplate($data);

		// Clear cache
		ClassRegistry::init('Upload')->clearCache();
		return true;
	}

	/**
	 * Get the CSS template and populate it with data.
	 *
	 * @param mixed $data The data to populate the template with.
	 *
	 * @return string The populated CSS template.
	 */
	private static function populateStyleTemplate($data) {

		// Get the data
		$file = new File(VECTORCMS_ROOT . 'View' . DS . 'Theme' . DS . static::CSS_FILENAME);
		$template = $file->read();

		// Replace the values
		foreach ($data as $var => $value) {
			if (is_string($value)) {
				$template = preg_replace('~\$\s*' . $var . '\s*\$~', $value, $template);
			}
		}

		// Write to app
		$themeStyle = new File(THEME_LOCATION . static::CSS_FILENAME);
		$themeStyle->write($template);

		return $template;
	}

	/**
	 * Set the upload.
	 *
	 * @param string $name  The name of the upload.
	 * @param string $token The token that references to the upload.
	 *
	 * @throws Exception
	 */
	private static function saveUpload($name, $token) {

		// Validate that the name of the upload is good
		if (!key_exists($name, static::getListOfUploads())) {
			throw new Exception("Unknown theme upload `{$name}`.");
		}

		// Validate file
		$file = Upload::loadFileFromToken($token);
		if ($file->getExtension(true) !== 'png') {
			throw new Exception("Only PNG files allowed");
		}

		// Remove previous file
		$destination = new File(THEME_LOCATION . static::getFilename($name));
		if ($destination->exists()) {
			$destination->delete();
		}

		$file->moveTo($destination->getPath());
	}

	/**
	 * Generate favicons for as many platforms as possible.
	 */
	private static function generateFavicons() {

		// Get the icon
		$icon = new File(THEME_LOCATION . static::getFilename('favicon'));
		if (!$icon->isReadable()) {
			throw new Exception("Favicon is not readable.");
		}

		# Delete previous icons
		$icons = new Dir(THEME_LOCATION . 'favicons');
		foreach ($icons->listDirectory() as $file) {
			$file->delete();
		}

		# Define the list of sizes, per platform
		$list = [
			'' => [ 16, 32, 96 ],
			'android' => [ 36, 48, 72, 96, 144, 192 ],
			'apple' => [ 57, 60, 72, 76, 114, 120, 144, 152, 180 ],
			'ms' => [ 144 ]
		];

		# Generate icons
		foreach ($list as $name => $sizes) {
			foreach ($sizes as $size) {

				// Resize using manicure
				$manicure = new Engine(new LocalSource($icon->getPath()));
				$manicure->quickApply([ [ 'Resize\Pad', $size, $size ] ]);
				$resizedIcon = $manicure->getResult()->getData();

				// Convert to valid PNG
				ob_start();
				imagepng($resizedIcon);
				$resizedIcon = ob_get_clean();

				// Write
				$filename = implode('-', array_filter([ $name, 'favicon', "{$size}x{$size}.png" ]));
				$file = new File(THEME_LOCATION . 'favicons' . DS . $filename);
				$file->write($resizedIcon);
			}
		}

		# Default icon
		$icon->copyTo(THEME_LOCATION . 'favicons' . DS . 'favicon.png');
	}

	/**
	 * Delete all existing favicons.
	 *
	 * @param bool $deleteOriginal True to delete the original one as well.
	 */
	public function deleteFavicons($deleteOriginal = false) {

		# Delete all previous icons
		$icons = glob(self::LOCATION . 'favicons/*');
		foreach ($icons as $icon)
			if ($deleteOriginal ? !preg_match('~(\.empty)$~', $icon) : !preg_match('~(original\.png|\.empty)$~', $icon)) {
				@unlink($icon);
			}
	}

	/**
	 * Get the final filename, based on the upload name.
	 *
	 * @param string $upload The name of the upload.
	 *
	 * @return string The full filename.
	 */
	private static function getFilename($upload) {
		return strtolower($upload) . '.png';
	}

	/**
	 * Get the path to an asset.
	 *
	 * @param string $name The name of the asset.
	 *
	 * @return string The full path to the asset.
	 */
	public static function asset($name) {
		return is_readable(THEME_LOCATION . $name)
			? THEME_LOCATION . $name
			: THEME_DEFAULT_LOCATION . $name;
	}

}
