<?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
 */

/**
 * Helper class
 *
 * This class encapsulates some commonly used methods.
 *
 * @ingroup form
 * @author  Christoph
 */
abstract class sly_Form_Helper {
	private static $select;       ///< sly_Form_Select_DropDown
	private static $user;         ///< sly_Model_User
	private static $type;         ///< string
	private static $hideOffline;  ///< boolean
	private static $clang;        ///< int
	private static $i18nLoaded = false;

	/**
	 * Create a single select box with all timezones
	 *
	 * @param  string $name              element's name
	 * @param  string $selected          the currently selected element or null for the system timezone
	 * @return sly_Form_Select_DropDown
	 */
	public static function getTimezoneSelect($name = 'timezone', $selected = null) {
		$selected = $selected === null ? sly_Core::getTimezone() : $selected;
		$list     = DateTimeZone::listIdentifiers();
		$list     = array_combine($list, $list);

		// transform all timezones from 'Continent/City' to 'Continent / City'
		// to make it easier to filter them via Chosen

		foreach ($list as $idx => $tz) {
			$list[$idx] = str_replace('/', ' / ', $tz);
		}

		return new sly_Form_Select_DropDown($name, t('timezone'), $selected, $list);
	}

	/**
	 * Creates a select element with all visible media categories
	 *
	 * @param  string         $name      the elements name
	 * @param  int            $root      the root category to use
	 * @param  sly_Model_User $user      the user (null for the current one)
	 * @param  string         $id        the elements ID
	 * @return sly_Form_Select_DropDown  the generated select element
	 */
	public static function getMediaCategorySelect($name, $root = null, sly_Model_User $user = null, $id = null) {
		if (!self::$i18nLoaded) {
			sly_Core::getI18N()->appendFile(SLY_SALLYFOLDER.'/backend/lang/pages/mediapool/');
			self::$i18nLoaded = true;
		}

		$init   = array(0 => t('no_category'));
		$select = new sly_Form_Select_DropDown($name, '', -1, $init, $id);

		if ($root === null) {
			$rootCats = sly_Util_MediaCategory::getRootCategories();
		}
		else {
			$service  = sly_Core::getContainer()->getMediaCategoryService();
			$rootCat  = $service->findById((int) $root);
			$rootCats = $rootCat ? array($rootCat) : null;
		}

		self::$select      = $select;
		self::$user        = $user;
		self::$type        = 'media';
		self::$hideOffline = false; // media cats cannot be offline
		self::$clang       = false; // media cats are monolingual

		if ($rootCats) {
			foreach ($rootCats as $rootCat) {
				self::walkTree($rootCat, 0);
			}
		}

		return $select;
	}

	/**
	 * Creates a select element with all visible categories
	 *
	 * @param  string         $name         the elements name
	 * @param  boolean        $hideOffline  true to hide offline categories
	 * @param  int            $clang        the clang to use
	 * @param  int            $root         the root category to use
	 * @param  sly_Model_User $user         the user (null for the current one)
	 * @param  string         $id           the elements ID
	 * @param  boolean        $addHomepage  whether or not to add the start article
	 * @return sly_Form_Select_DropDown     the generated select element
	 */
	public static function getCategorySelect($name, $hideOffline = true, $clang = null, $root = null, sly_Model_User $user = null, $id = null, $addHomepage = true) {
		$values = array();
		if ($addHomepage) $values[0] = t('start_article');
		$select = new sly_Form_Select_DropDown($name, '', -1, $values, $id);

		if ($root === null) {
			$rootCats = sly_Util_Category::getRootCategories($hideOffline, $clang);
		}
		else {
			$rootCat  = sly_Util_Category::findById((int) $root, $clang);
			$rootCats = $rootCat ? array($rootCat) : null;
		}

		self::$select      = $select;
		self::$user        = $user;
		self::$type        = 'structure';
		self::$hideOffline = (boolean) $hideOffline;
		self::$clang       = $clang;

		if ($rootCats) {
			foreach ($rootCats as $rootCat) {
				self::walkTree($rootCat, 0);
			}
		}

		return $select;
	}

	/**
	 * Creates a select element with all visible languages
	 *
	 * @param  string         $name      the elements name
	 * @param  sly_Model_User $user      the user (null for the current one)
	 * @param  string         $id        the elements ID
	 * @return sly_Form_Select_DropDown  the generated select element
	 */
	public static function getLanguageSelect($name, sly_Model_User $user = null, $id = null) {
		$langs = array();
		$user  = $user ? $user : sly_Util_User::getCurrentUser();

		foreach (sly_Util_Language::findAll() as $langID => $lang) {
			if ($user && sly_Util_Language::hasPermissionOnLanguage($user, $langID)) {
				$langs[$langID] = $lang->getName();
			}
		}

		return new sly_Form_Select_DropDown($name, '', -1, $langs, $id);
	}

	/**
	 * Helper function
	 *
	 * This method implements the tree walking algorithm used for both selects.
	 * It pays attention to the csw[] permissions.
	 *
	 * @param mixed $category  the current category (media or structure)
	 * @param int   $depth     current depth (to indent <option> elements)
	 */
	private static function walkTree($category, $depth) {
		if (empty($category)) return;

		if (self::canSeeCategory($category)) {
			$name = $category->getName();
			self::$select->addValue($category->getId(), str_repeat(' ', $depth*2).$name);
		}

		$children = $category->getChildren(self::$hideOffline, self::$clang);

		if (is_array($children)) {
			foreach ($children as $child) {
				self::walkTree($child, $depth + 1);
			}
		}
	}

	/**
	 * Check admin permission
	 *
	 * This method checks whether the user is admin[] or has the appropriate root
	 * category permission (media[0] or cws[o]).
	 *
	 * @return boolean  true or false
	 */
	private static function isAdmin() {
		if (!self::$user) return false;

		$isAdmin = self::$user->isAdmin();

		switch (self::$type) {
			case 'media':     $isAdmin |= self::$user->hasRight('mediacategory', 'access' , sly_Authorisation_ListProvider::ALL); break;
			case 'structure': $isAdmin |= sly_Util_Article::canEditArticle(self::$user, 0); break;
		}

		return $isAdmin;
	}

	/**
	 * Checks category visibility
	 *
	 * This method checks whether the user can view a given category.
	 *
	 * @param  mixed $category  the current category (media or structure)
	 * @return boolean          true or false
	 */
	private static function canSeeCategory($category) {
		if (self::$user === null || self::isAdmin()) return true;

		switch (self::$type) {
			case 'media':
				return self::$user->hasRight('mediacategory', 'access', $category->getId());

			case 'structure':
				return sly_Util_Category::canReadCategory(self::$user, $category->getId());

			default:
				return true;
		}
	}

	/**
	 * Parse form values
	 *
	 * This method is useful for parsing multilingual form elements. Multilingual
	 * elements consist of (N+1) elements plus a special checkbox for "use the
	 * same for all languages". This method checks this checkbox and returns the
	 * same value or the value of each form element.
	 *
	 * For monolingual elements, the value of the first element is returned. This
	 * is the default. For multilingual elements, you always get an array with
	 * the values for each language (even if the checkbox is checked and
	 * therefore the value is the same for all languages). This makes it easier
	 * to code against the form, knowing that multilingual elements *always*
	 * return an array.
	 *
	 * @param  string  $name          the element name
	 * @param  string  $default       default value if not present in POST
	 * @param  boolean $multilingual  toggles the multilingual parsing algorithm
	 * @param  string  $nameSuffix    a string that will be appened to the generated element names when working
	 *                                in multilingual mode; use this only if you know what you're doing (mainly
	 *                                (used for complex elements that append strings to the element name, like
	 *                                the old datepicker or the varisale money input which consists of an input
	 *                                and a select field)
	 * @param  sly_Request $request   the request to use or null for the global one
	 * @return mixed                  the value as described above
	 */
	public static function parseFormValue($name, $default = null, $multilingual = false, $nameSuffix = '', sly_Request $request = null) {
		$monoName  = $name.$nameSuffix;
		$request   = $request ? $request : sly_Core::getRequest();
		$monoValue = $request->post($monoName, 'raw', $default);

		if (!$multilingual) {
			return $monoValue;
		}

		$equal  = !sly_Util_Language::isMultilingual() || $request->post('equal__'.$name, 'boolean', false);
		$values = array();

		foreach (sly_Util_Language::findAll(true) as $clangID) {
			$key              = $name.'__clang_'.$clangID.$nameSuffix;
			$values[$clangID] = $equal ? $monoValue : $request->post($key, 'raw', $default);
		}

		return $values;
	}
}
