<?php
/*
 * Copyright (c) 2013, 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
 */

abstract class WV_Datatype {
	const FILTER_EXACTMATCH = 1;
	const FILTER_NOCASE     = 2;
	const FILTER_BY_KEY     = 4;

	private static $datatypes = null;
	private static $instances = array();

	public static function getDatatypes() {
		if (self::$datatypes === null) {
			self::$datatypes = sly_Core::config()->get('developer_utils/datatypes', array());
		}

		return self::$datatypes;
	}

	/**
	 * Datentyp-Klasse ermitteln
	 *
	 * Diese Methode gibt für einen Key den Klassennamen eines Datentyps zurück.
	 *
	 * @throws WV_Exception_Datatype    falls der Datentyp nicht existiert
	 * @param  string $identifier       der Key des Datentyps
	 * @return string                   Klassenname
	 */
	public static function getClass($identifier) {
		if (!self::exists($identifier)) {
			throw new WV_Exception_Datatype('Datatype '.$identifier.' could not be found.');
		}

		$datatypes = self::getDatatypes();
		return $datatypes[$identifier];
	}

	/**
	 * Prüft, ob Datentyp existiert
	 *
	 * @param  string $identifier  der Key des Datentyps
	 * @param  bool   $checkClass  wenn true wird auch geprüft ob die Klasse existiert
	 * @return bool                true, wenn der Datentyp existiert, sonst false
	 */
	public static function exists($identifier, $checkClass = false) {
		$datatypes = self::getDatatypes();
		return isset($datatypes[$identifier]) && (!$checkClass || class_exists($datatypes[$identifier]));
	}

	/**
	 * Datentyp-Instanz erzeugen
	 *
	 * @throws WV_Exception_Datatype  falls der Datentyp nicht existiert
	 * @param  string $identifier     der Key des Datentyps
	 * @return WV_IDatatype           der Datentyp
	 */
	public static function factory($identifier) {
		if (!isset(self::$instances[$identifier])) {
			$impl = self::getClass($identifier);

			if (!class_exists($impl)) {
				throw new WV_Exception_Datatype(t('devutils_datatype_not_loadable', $impl));
			}

			self::$instances[$identifier] = new $impl();
		}

		return self::$instances[$identifier];
	}

	public static function call($identifier, $method, $params = array()) {
		if (!self::exists($identifier, true)) return null;

		$datatype = self::factory($identifier);

		self::initJavaScript();

		return call_user_func_array(array($datatype, $method), $params);
	}

	public static function initJavaScript() {
		static $init = false;

		// Let's make sure we don't have to call array_unique() again and again
		// when adding the same file.

		if ($init === false && sly_Core::isBackend()) {
			$layout = sly_Core::getLayout();
			$base   = sly_Util_AddOn::assetBaseUri('webvariants/developer-utils');
			$init   = true;

			$layout->addJavaScriptFile($base.'js/datatypes.min.js');
		}
	}

	/**
	 * get list of avialable placeholders
	 *
	 * @param  sly_Form             $form
	 * @param  array                $params
	 * @param  WV_Property          $prop
	 * @param  sly_Form_ElementBase $element
	 * @return WV_Form_PropertyHelper
	 */
	public static function placeholders(sly_Form $form, array $params, WV_Property $prop = null, sly_Form_ElementBase $element = null) {
		$format  = isset($params['format']) ? $params['format'] : '#%s#';
		$helper  = new WV_Form_PropertyHelper(array(), $format);
		$service = sly_Service_Factory::getAddOnService();

		if (isset($params['label'])) {
			$helper->setLabel($params['label']);
		}

		if (isset($params['groups'])) {
			foreach ($params['groups'] as $group => $config) {
				if (in_array($group, array('frontenduser', 'metainfo', 'varisale'))) {
					if (!$service->isAvailable('webvariants/'.$group)) continue;
				}

				switch ($group) {
					case 'frontenduser':
						// $config is the user type
						if ($config === null) {
							$helper->addFrontendUserAttributes(null);
						}
						else {
							foreach (sly_makeArray($config) as $type) {
								$helper->addFrontendUserAttributes($type);
							}
						}
						break;

					case 'metainfo':
						// $config is an array of metainfo types
						foreach ($config as $metainfo => $mconfig) {
							switch ($metainfo) {
								case 'articles':
									if ($mconfig === null) {
										$helper->addArticleMetainfos(null);
									}
									else {
										foreach (sly_makeArray($mconfig) as $type) {
											$helper->addArticleMetainfos($type);
										}
									}
									break;

								case 'categories': $helper->addCategoryMetainfos(); break;
								case 'media':      $helper->addMediumMetainfos();   break;
								case 'users':      $helper->addUserMetainfos();     break;

								default:
									throw new Exception('Invalid metainfo "'.$metainfo.'" given.');
							}
						}
						break;

					case 'varisale':
						$helper->addVarisaleAttributes();
						break;

					default:
						throw new Exception('Invalid group "'.$metainfo.'" given.');
				}
			}
		}

		if ($element) {
			$helper->prepareFormElement($element);
		}

		return $helper;
	}

	public static function setHelpTextOnElement(sly_Form $form, sly_Form_ElementBase $element, WV_IProperty $property) {
		$help = $property->getHelpText();

		if (is_string($help)) {
			$element->setHelpText($help);
		}
		elseif (isset($help['type']) == 'propertylist') {
			$format = isset($help['params']['format']) ? $help['params']['format'] : '#%s#';
			$helper = new WV_Form_PropertyHelper(array(), $format);

			if (isset($help['params']['label'])) {
				$helper->setLabel($help['params']['label']);
			}

			foreach ($help['params']['properties'] as $name => $info) {
				$helper->addProperty($name, $info);
			}

			$helper->prepareFormElement($element);
		}
		else {
			if (!isset($help['callback'])) {
				throw new Exception(t('devutils_property_helptext_must_be_string_or_callback'), gettype($help));
			}

			// callback variant
			$callback = $help['callback'];
			$params   = isset($help['params']) ? $help['params'] : array();

			call_user_func_array($callback, array($form, $params, $property, $element));
		}
	}
}
