<?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_Sally {
	const CLANG_CURRENT = -1; ///< int  Pseudo-Sprache für die aktuelle Sprache
	const CLANG_ALL     = -2; ///< int  Pseudo-Sprache für "jede Sprache"

	public static function init() {
		// CSS initialisieren

		$layout = sly_Core::getLayout();
		$base   = sly_Util_AddOn::assetBaseUri('webvariants/developer-utils');

		$layout->addCSSFile($base.'css/backend.less');

		// add documentation backend page

		$disable = sly_Core::config()->get('developer_utils/disable_docs', false);
		$nav     = $layout->getNavigation();
		$group   = $nav->getGroup('system');

		if (!$disable) {
			if ($group) {
				$nav->addPage('system', 'devutilsdocs', 'Dokumentation');
			}

			$disable = sly_Core::config()->get('developer_utils/disable_slice_tooltip', false);

			if (!$disable) {
				$dispatcher = sly_Core::dispatcher();
				$listener   = array(__CLASS__, 'addDocTooltip');
				$dispatcher->register('SLY_SLICE_POSTVIEW_ADD',  $listener);
				$dispatcher->register('SLY_SLICE_POSTVIEW_EDIT', $listener);
			}
		}

		// add log viewer

		if ($group) {
			$page = $nav->get('system', 'system');
			if ($page) $page->addSubpage('devutilslogviewer', 'Log-Viewer');
		}
	}

	public static function addDocTooltip(array $params) {
		$service = sly_Service_Factory::getModuleService();
		$desc    = $service->get($params['module'], 'description', '');

		if (!empty($desc)) {
			$layout = sly_Core::getLayout();
			$base   = sly_Util_AddOn::assetBaseUri('webvariants/developer-utils');

			$layout->addJavaScriptFile($base.'js/jquery.tooltip.min.js');

			include DEVUTILS_PATH.'views/tooltip.phtml';
		}
	}

	public static function isAjaxRequest() {
		return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
	}

	/**
	 * Wandelt die symbolischen Sprach-Konstanten in echte Clang-IDs
	 *
	 * Diese Hilfsmethode befreit alle anderen Methoden des AddOns von der
	 * lästigen Aufgabe, die als Argument emfpangene (evtl. symbolische)
	 * Sprachkonstante in die aktuelle Sprache umzuformen.
	 *
	 * @param  int $clang  die Sprach-Konstante (>= 0 = echte Clang-ID, sonst symbolische Konstante)
	 * @return int         die echte Sprache, falls WV_Sally::CLANG_CURRENT, null oder false übergeben wurde
	 */
	public static function clang($clang = null) {
		return ($clang === self::CLANG_CURRENT || $clang === false || $clang === null) ? sly_Core::getCurrentClang() : intval($clang);
	}

	public static function clearOutput() {
		while (ob_get_level()) ob_end_clean();
	}

	/**
	 * @return sly_Model_Category
	 */
	public static function getCurrentCategory() {
		$id = self::getCurrentCategoryID();
		return $id >= 0 ? sly_Util_Category::findById($id) : null;
	}

	/**
	 * @return int
	 */
	public static function getCurrentCategoryID() {
		$article = sly_Core::getCurrentArticle();
		return $article ? $article->getCategoryId() : -1;
	}

	public static function isBackend() {
		$lucene = class_exists('WV11') ? WV11::isRunning() : false;
		return sly_Core::isBackend() && !$lucene;
	}

	/**
	 * @deprecated since 5.2, will be removed in 6.0
	 * @return boolean
	 */
	public static function isFrontend() {
		return !self::isBackend();
	}

	public static function filterArticles(WV_IFilter $filter, $sortby = 'article.pos', $direction = 'ASC', $limitClause = '') {
		$tables = array('article');
		$tables = array_unique(array_merge($tables, $filter->getRequiredTables()));

		// Query zusammensetzen

		$prefix   = WV_SQL::getPrefix();
		$sql      = WV_SQL::getInstance();
		$articles = array();
		$query    = 'SELECT '.$prefix.'article.id, '.$prefix.'article.clang '.
			'FROM '.self::constructFilterQuery($tables, $connectors, $filter, 'article').' '.
			'ORDER BY '.$prefix.$sortby.' '.$direction.' '.$limitClause;

		$sql->query($query);

		foreach ($sql as $row) {
			$articles[] = sly_Util_Article::findById($row['id'], $row['clang']);
		}

		return $articles;
	}

	protected static function constructFilterQuery(&$tables, &$connectors, $filter, $masterTable) {
		$prefix         = WV_SQL::getPrefix();
		$joinConditions = array('1');

		foreach ($tables as &$table) {
			if ($table != $masterTable) { // Die Haupttabelle muss sich nicht an sich selbst joinen.
				$joinConditions[] = str_replace('#_', $prefix, $filter->getJoinCondition($table, $masterTable));
			}

			$table = $prefix.$table;
		}

		$tmp    = new WV_Filter_And(array());
		$join   = implode(' AND ', $tmp->optimize($joinConditions));
		$filter = implode(' AND ', $tmp->optimize(array($filter->toSQL())));
		$where  = $tmp->optimize(array($join, $filter));
		$where  = array_values(array_filter($where));
		$where  = empty($where) ? '1' : (count($where) === 1 ? $where[0] : ('('.implode(') AND (', $where).')'));

		return implode(', ', $tables).' WHERE '.$where;
	}

	/**
	 * Artikelliste ermitteln
	 *
	 * Diese Methode erzeugt eine Liste von sly_Model_Article-Objekten basierend
	 * auf den gegebenen Filterkriterien zurück. Das Sortierkriterium muss eine
	 * Spalte in der Tabelle sly_article sein.
	 *
	 * @param  mixed    $category     die Artikelkategorie
	 * @param  boolean  $onlineOnly   wenn true, werden nur online Artikel berücksichtigt (default: true)
	 * @param  string   $sortby       das Sortierkriterium (aus der Relation article oder wv2_article_type)
	 * @param  string   $direction    die Sortierrichtung
	 * @param  string   $limitClause  eine optionale "LIMIT a,b"-Angabe
	 * @param  int      $clang        die gewünschte Sprache
	 * @return array                  Liste von passenden sly_Model_Article-Objekten
	 */
	public static function getArticlesByCategory($category, $sortby = 'updatedate', $direction = 'ASC', $limitClause = '', $onlineOnly = true, $clang = null) {
		$filter = new WV_Filter_And(
			new WV_Filter_Article_Column('article.re_id', self::getIDForCategory($category), '='),
			new WV_Filter_Article_CLang(self::clang($clang))
		);

		if ($onlineOnly) $filter->add(new WV_Filter_Article_Online());
		return self::filterArticles($filter, 'article.'.$sortby, $direction, $limitClause);
	}

	/**
	 * @deprecated  since 5.2, use sly_Util_HTTP instead
	 */
	public static function getUrl($targetArticle, $clang = null, $parameters = array(), $divider = '&amp;') {
		$clang = self::clang($clang);
		return sly_Util_HTTP::getUrl($targetArticle, $clang, $parameters, $divider);
	}

	/**
	 * @deprecated  since 5.2, use sly_Util_HTTP instead
	 */
	public static function getAbsoluteUrl($targetArticle, $clang = null, $parameters = array(), $divider = '&amp;') {
		$clang = self::clang($clang);
		return sly_Util_HTTP::getAbsoluteUrl($targetArticle, $clang, $parameters, $divider);
	}

	/**
	 * @deprecated  since 5.2, use sly_Util_HTTP instead
	 */
	public static function redirect($target, $parameters = array(), $code = 302) {
		sly_Util_HTTP::redirect($target, $parameters, '', $code);
	}

	/**
	 * Ermitteln einer Artikel-ID
	 *
	 * Diese Methode ermittelt zu einem Artikel die dazugehörige ID.
	 *
	 * @param  mixed $article  sly_Model_Article oder int
	 * @return int             die ID oder -1 falls keine gefunden wurde
	 */
	public static function getIDForArticle($article) {
		if (sly_Util_String::isInteger($article))  return (int) $article;
		if ($article instanceof sly_Model_Article) return (int) $article->getId();

		trigger_error(t('devutils_cant_identify_article', $article, gettype($article)), E_USER_WARNING);
		return -1;
	}

	public static function getIDForCategory($category) {
		if (sly_Util_String::isInteger($category))   return (int) $category;
		if ($category instanceof sly_Model_Category) return (int) $category->getId();

		trigger_error(t('devutils_cant_identify_category', $category, gettype($category)), E_USER_WARNING);
		return -1;
	}

	public static function getLoginForUser($user) {
		if (is_string($user))                return (string) $user;
		if ($user instanceof sly_Model_User) return (string) $user->getLogin();

		trigger_error(t('devutils_cant_identify_user', $user, gettype($user)), E_USER_WARNING);
		return null;
	}

	/**
	 * Ermitteln einer Datei-ID
	 *
	 * Diese Methode ermittelt zu einem Dateinamen die dazugehörige ID.
	 *
	 * @param  mixed $medium  der Dateiname (ohne Pfad!) (String) oder die ID (wird als int ungeändert zurückgegeben)
	 * @return int            die ID oder -1 falls keine gefunden wurde
	 */
	public static function getIDForMedium($medium) {
		if (sly_Util_String::isInteger($medium)) return (int) $medium;
		if ($medium instanceof sly_Model_Medium) return (int) $medium->getId();

		if (is_string($medium)) {
			$db = sly_DB_Persistence::getInstance();
			$id = $db->magicFetch('file', 'id', array('filename' => $medium));

			if ($id !== false) return (int) $id;
		}

		trigger_error('Konnte Datei-ID für "'.$medium.'" ('.gettype($medium).') nicht ermitteln!', E_USER_WARNING);
		return -1;
	}

	public static function enforceMethod($method) {
		if (strtoupper($method) !== $_SERVER['REQUEST_METHOD']) {
			throw new WV_Exception_Sally('Die Anfrage muss via '.strtoupper($method).' erfolgen.');
		}
	}

	/**
	 * Returns an Article for an Article Identifier
	 *
	 * @param  mixed $articleIdent  Article ID, sly_Model_Article or rex_article
	 * @param  int   $clang         desired target language
	 * @return sly_Model_Article    desired article object
	 */
	public static function getArticle($articleIdent, $clang = null) {
		$id    = self::getIDForArticle($articleIdent);
		$clang = self::clang($clang);

		return sly_Util_Article::findById($id, $clang);
	}

	/**
	 * Gibt die Anzahl aller Artikel einer Kategorie zurück.
	 *
	 * @param  int  $categoryID  die Kategorie
	 * @param  bool $onlineOnly  wenn true, werden nur online Artikel berücksichtigt
	 * @return int               Anzahl von Artikeln in der Kategorie
	 */
	public static function getArticlesCountByCategory($categoryID, $onlineOnly = false) {
		$where = 're_id = ? AND startpage = 1'.($onlineOnly ? ' AND status = 1' : '');
		return WV_SQL::getInstance()->count('article', $where, $categoryID);
	}

	public static function bootcache() {
		sly_Util_BootCache::addClass('WV_Sally');
	}
}
