<?php
/*
* 2007-2012 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
*  @author PrestaShop SA <contact@prestashop.com>
*  @copyright  2007-2012 PrestaShop SA
*  @version  Release: $Revision: 16943 $
*  @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
*  International Registered Trademark & Property of PrestaShop SA
*/

class CMSCategoryCore extends ObjectModel
{
	public $id;

	/** @var integer CMSCategory ID */
	public $id_cms_category;

	/** @var mixed Name */
	public $name;

	/** @var boolean Status for display */
	public $active = true;

	/** @var mixed Description */
	public $description;

	/** @var integer Parent CMSCategory ID */
	public $id_parent;
	
	/** @var  integer category position */
	public $position;

	/** @var integer Parents number */
	public $level_depth;

	/** @var mixed URL Rewriting */
	public $link_rewrite;

	/** @var mixed Meta title */
	public $meta_title;

	/** @var mixed Meta keywords */
	public $meta_keywords;

	/** @var mixed Meta description */
	public $meta_description;

	/** @var string Object creation date */
	public $date_add;

	/** @var string Object last modification date */
	public $date_upd;

	protected static $_links = array();

	protected $tables = array ('cms_category', 'cms_category_lang');

	protected $fieldsRequired = array('id_parent', 'active');
 	protected $fieldsSize = array('id_parent' => 10, 'active' => 1);
 	protected $fieldsValidate = array('active' => 'isBool', 'id_parent' => 'isUnsignedInt');
	protected $fieldsRequiredLang = array('name', 'link_rewrite');
 	protected $fieldsSizeLang = array('name' => 64, 'link_rewrite' => 64, 'meta_title' => 128, 'meta_description' => 255, 'meta_keywords' => 255);
 	protected $fieldsValidateLang = array('name' => 'isCatalogName', 'link_rewrite' => 'isLinkRewrite', 'description' => 'isCleanHtml',
	'meta_title' => 'isGenericName', 'meta_description' => 'isGenericName', 'meta_keywords' => 'isGenericName');

	protected $table = 'cms_category';
	protected $identifier = 'id_cms_category';

	public function getFields()
	{
		parent::validateFields();
		if (isset($this->id))
			$fields['id_cms_category'] = (int)$this->id;
		$fields['active'] = (int)$this->active;
		$fields['id_parent'] = (int)$this->id_parent;
		$fields['position'] = (int)$this->position;
		$fields['level_depth'] = (int)$this->level_depth;
		$fields['date_add'] = pSQL($this->date_add);
		$fields['date_upd'] = pSQL($this->date_upd);
		return $fields;
	}

	/**
	  * Check then return multilingual fields for database interaction
	  *
	  * @return array Multilingual fields
	  */
	public function getTranslationsFieldsChild()
	{
		parent::validateFieldsLang();
		return parent::getTranslationsFields(array('name', 'description', 'link_rewrite', 'meta_title', 'meta_keywords', 'meta_description'));
	}

	public function add($autodate = true, $nullValues = false)
	{
		$this->position = self::getLastPosition((int)$this->id_parent);
		$this->level_depth = $this->calcLevelDepth();
		foreach ($this->name as $k => $value)
			if (preg_match('/^[1-9]\./', $value))
				$this->name[$k] = '0'.$value;
		$ret = parent::add($autodate);
		$this->cleanPositions($this->id_parent);
		return $ret;
	}

	public function update($nullValues = false)
	{
		$this->level_depth = $this->calcLevelDepth();
		foreach ($this->name as $k => $value)
			if (preg_match('/^[1-9]\./', $value))
				$this->name[$k] = '0'.$value;
		return parent::update();
	}

	/**
	  * Recursive scan of subcategories
	  *
	  * @param integer $max_depth Maximum depth of the tree (i.e. 2 => 3 levels depth)
 	  * @param integer $current_depth specify the current depth in the tree (don't use it, only for rucursivity!)
	  * @param array $excluded_ids_array specify a list of ids to exclude of results
 	  * @param integer $id_lang Specify the id of the language used
	  *
 	  * @return array Subcategories lite tree
	  */
	public function recurseLiteCategTree($max_depth = 3, $current_depth = 0, $id_lang = null, $excluded_ids_array = null)
	{
		global $link;

		$id_lang = !(int)$id_lang ? _USER_ID_LANG_ : (int)$id_lang;

		$children = array();
		if (($max_depth == 0 || $current_depth < $max_depth) && $subcats = $this->getSubCategories((int)$id_lang, true))
			if (count($subcats)) /* Keep this line out of the first if statement to avoid side effect due to the assignation */
				foreach ($subcats as &$subcat)
					if (!$subcat['id_cms_category'])
						break;
					elseif (!is_array($excluded_ids_array) || !in_array($subcat['id_cms_category'], $excluded_ids_array))
					{
						$categ = new CMSCategory((int)$subcat['id_cms_category'], (int)$id_lang);
						$categ->name = CMSCategory::hideCMSCategoryPosition($categ->name);
						$children[] = $categ->recurseLiteCategTree($max_depth, $current_depth + 1, (int)$id_lang, $excluded_ids_array);
					}

		return array('id' => (int)$this->id_cms_category, 'link' => $link->getCMSCategoryLink($this->id, $this->link_rewrite),
		'name' => $this->name, 'desc'=> $this->description, 'children' => $children);
	}

	public static function getRecurseCategory($id_lang = _USER_ID_LANG_, $current = 1, $active = true, $links = 0)
	{
		$category = Db::getInstance()->getRow('
		SELECT c.`id_cms_category`, c.`id_parent`, c.`level_depth`, cl.`name`, cl.`link_rewrite`
		FROM `'._DB_PREFIX_.'cms_category` c
		JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
		WHERE c.`id_cms_category` = '.(int)$current.'
		AND `id_lang` = '.(int)$id_lang);

		$result = Db::getInstance()->ExecuteS('
		SELECT c.`id_cms_category`
		FROM `'._DB_PREFIX_.'cms_category` c
		WHERE c.`id_parent` = '.(int)$current.
		($active ? ' AND c.`active` = 1' : ''));

		foreach ($result as $row)
			$category['children'][] = self::getRecurseCategory((int)$id_lang, $row['id_cms_category'], $active, $links);

		$category['cms'] = Db::getInstance()->ExecuteS('
		SELECT c.`id_cms`, cl.`meta_title`, cl.`link_rewrite`
		FROM `'._DB_PREFIX_.'cms` c
		JOIN `'._DB_PREFIX_.'cms_lang` cl ON c.`id_cms` = cl.`id_cms`
		WHERE `id_cms_category` = '.(int)$current.'
		AND cl.`id_lang` = '.(int)$id_lang.($active ? ' AND c.`active` = 1' : '').'
		ORDER BY c.`position`');

		if ($links == 1)
		{
			$link = new Link();
			$category['link'] = $link->getCMSCategoryLink($current, $category['link_rewrite']);
			foreach ($category['cms'] as $key => $cms)
				$category['cms'][$key]['link'] = $link->getCMSLink($cms['id_cms'], $cms['link_rewrite']);
		}

		return $category;
	}

	public static function recurseCMSCategory($categories, $current, $id_cms_category = 1, $id_selected = 1, $is_html = 0)
	{
		$html = '<option value="'.$id_cms_category.'"'.(($id_selected == $id_cms_category) ? ' selected="selected"' : '').'>'.
		str_repeat('&nbsp;', $current['infos']['level_depth'] * 5).self::hideCMSCategoryPosition(stripslashes($current['infos']['name'])).'</option>';
		if ($is_html == 0)
			echo $html;
		if (isset($categories[$id_cms_category]))
			foreach (array_keys($categories[$id_cms_category]) as $key)
				$html .= self::recurseCMSCategory($categories, $categories[$id_cms_category][$key], $key, $id_selected, $is_html);
		return $html;
	}
	
	

	/**
	  * Recursively add specified CMSCategory childs to $toDelete array
	  *
	  * @param array &$toDelete Array reference where categories ID will be saved
	  * @param array $id_cms_category Parent CMSCategory ID
	  */
	protected function recursiveDelete(&$toDelete, $id_cms_category)
	{
	 	if (!is_array($toDelete) || !$id_cms_category)
	 		die(Tools::displayError());

		$result = Db::getInstance()->ExecuteS('
		SELECT `id_cms_category`
		FROM `'._DB_PREFIX_.'cms_category`
		WHERE `id_parent` = '.(int)$id_cms_category.' AND `id_parent` != `id_category`');

		foreach ($result as $row)
		{
			$toDelete[] = (int)$row['id_cms_category'];
			$this->recursiveDelete($toDelete, (int)$row['id_cms_category']);
		}
	}

	public function delete()
	{
		if ($this->id == 1) return false;
		
		$this->clearCache();

		/* Get childs categories */
		$toDelete = array((int)$this->id);
		$this->recursiveDelete($toDelete, (int)$this->id);
		$toDelete = array_unique($toDelete);

		/* Delete CMS Category and its child from database */
		$list = count($toDelete) > 1 ? implode(',', $toDelete) : (int)$this->id;
		Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'cms_category` WHERE `id_cms_category` IN ('.$list.')');
		Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'cms_category_lang` WHERE `id_cms_category` IN ('.$list.')');

		self::cleanPositions($this->id_parent);
		
		/* Delete pages which are in categories to delete */
		$result = Db::getInstance()->ExecuteS('
		SELECT `id_cms`
		FROM `'._DB_PREFIX_.'cms`
		WHERE `id_cms_category` IN ('.$list.')');
		foreach ($result as $p)
		{
			$product = new CMS((int)$p['id_cms']);
			if (Validate::isLoadedObject($product))
				$product->delete();
		}
		return true;
	}
	
	/**
	 * Delete several categories from database
	 *
	 * return boolean Deletion result
	 */
	public function deleteSelection($categories)
	{
		$return = 1;
		foreach ($categories as $id_category_cms)
		{
			$category_cms = new CMSCategory((int)$id_category_cms);
			$return &= $category_cms->delete();
		}
		return $return;
	}

	/**
	  * Get the number of parent categories
	  *
	  * @return integer Level depth
	  */
	public function calcLevelDepth()
	{
		$parentCMSCategory = new CMSCategory((int)$this->id_parent);
		if (!$parentCMSCategory)
			die('parent CMS Category does not exist');
		return $parentCMSCategory->level_depth + 1;
	}

	/**
	  * Return available categories
	  *
	  * @param integer $id_lang Language ID
	  * @param boolean $active return only active categories
	  * @return array Categories
	  */
	public static function getCategories($id_lang, $active = true, $order = true)
	{
	 	if (!Validate::isBool($active))
	 		die(Tools::displayError());

		$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
		SELECT *
		FROM `'._DB_PREFIX_.'cms_category` c
		LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
		WHERE `id_lang` = '.(int)$id_lang.'
		'.($active ? 'AND `active` = 1' : '').'
		ORDER BY `name` ASC');

		if (!$order)
			return $result;

		$categories = array();
		foreach ($result as $row)
			$categories[$row['id_parent']][$row['id_cms_category']]['infos'] = $row;
		return $categories;
	}

	public static function getSimpleCategories($id_lang)
	{
		return Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
		SELECT c.`id_cms_category`, cl.`name`
		FROM `'._DB_PREFIX_.'cms_category` c
		LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category`)
		WHERE cl.`id_lang` = '.(int)$id_lang.'
		ORDER BY cl.`name`');
	}

	/**
	  * Return current CMSCategory childs
	  *
	  * @param integer $id_lang Language ID
	  * @param boolean $active return only active categories
	  * @return array Categories
	  */
	public function getSubCategories($id_lang, $active = true)
	{
		$subcategories = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
		SELECT c.*, cl.id_lang, cl.name, cl.description, cl.link_rewrite, cl.meta_title, cl.meta_keywords, cl.meta_description
		FROM `'._DB_PREFIX_.'cms_category` c
		LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = '.(int)$id_lang.')
		WHERE `id_parent` = '.(int)$this->id.'
		'.($active ? 'AND `active` = 1' : '').'
		GROUP BY c.`id_cms_category`
		ORDER BY `name` ASC');

		/* Remove numbers from the titles */
		foreach ($subcategories as &$subcategory)
			$subcategory['name'] = CMSCategory::hideCMSCategoryPosition($subcategory['name']);

		return $subcategories;
	}

	/**
	  * Hide CMSCategory prefix used for position
	  *
	  * @param string $name CMSCategory name
	  * @return string Name without position
	  */
	public static function hideCMSCategoryPosition($name)
	{
		return preg_replace('/^[0-9]+\./', '', $name);
	}

	/**
	  * Return main categories
	  *
	  * @param integer $id_lang Language ID
	  * @param boolean $active return only active categories
	  * @return array categories
	  */
	public static function getHomeCategories($id_lang, $active = true)
	{
		return self::getChildren(1, $id_lang, $active);
	}
	/**
	 * @deprecated
	**/
	public static function getRootCMSCategory($id_lang = null)
	{
		Tools::displayAsDeprecated();

		$id_lang = is_null($id_lang) ? _USER_ID_LANG_ : (int)$id_lang;
		return new CMSCategory (1, $id_lang);
	}

	public static function getChildren($id_parent, $id_lang, $active = true)
	{
		$results = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
		SELECT c.`id_cms_category`, cl.`name`, cl.`link_rewrite`
		FROM `'._DB_PREFIX_.'cms_category` c
		LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
		WHERE `id_lang` = '.(int)$id_lang.' AND c.`id_parent` = '.(int)$id_parent.'
		'.($active ? 'AND `active` = 1' : '').'
		ORDER BY `name` ASC');

		/* Remove numbers from the titles */
		foreach ($results as &$row)
			$row['name'] = CMSCategory::hideCMSCategoryPosition($row['name']);

		return $results;
	}

	/**
	  * Check if CMSCategory can be moved in another one
	  *
	  * @param integer $id_parent Parent candidate
	  * @return boolean Parent validity
	  */
	public static function checkBeforeMove($id_cms_category, $id_parent)
	{
		if ($id_cms_category == $id_parent)
			return false;
		if ($id_parent == 1)
			return true;
		$i = (int)$id_parent;

		while (42)
		{
			$result = Db::getInstance()->getRow('SELECT `id_parent` FROM `'._DB_PREFIX_.'cms_category` WHERE `id_cms_category` = '.(int)$i);
			if (!isset($result['id_parent'])) return false;
			if ($result['id_parent'] == $id_cms_category) return false;
			if ($result['id_parent'] == 1) return true;
			$i = $result['id_parent'];
		}
	}

	public static function getLinkRewrite($id_cms_category, $id_lang)
	{
		if (!Validate::isUnsignedId($id_cms_category) || !Validate::isUnsignedId($id_lang))
			return false;

		if (isset(self::$_links[$id_cms_category.'-'.$id_lang]))
			return self::$_links[$id_cms_category.'-'.$id_lang];

		$result = Db::getInstance()->getRow('
		SELECT cl.`link_rewrite`
		FROM `'._DB_PREFIX_.'cms_category` c
		LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
		WHERE `id_lang` = '.(int)$id_lang.'
		AND c.`id_cms_category` = '.(int)$id_cms_category);

		self::$_links[$id_cms_category.'-'.$id_lang] = $result['link_rewrite'];

		return $result['link_rewrite'];
	}

	public function getLink()
	{
		global $link;
		return $link->getCMSCategoryLink($this->id, $this->link_rewrite);
	}

	public function getName($id_lang = null)
	{
		if (!$id_lang)
		{
			global $cookie;

			if (isset($this->name[$cookie->id_lang]))
				$id_lang = $cookie->id_lang;
			else
				$id_lang = (int)_PS_LANG_DEFAULT_;
		}
		return isset($this->name[$id_lang]) ? $this->name[$id_lang] : '';
	}

	/**
	  * Light back office search for categories
	  *
	  * @param integer $id_lang Language ID
	  * @param string $query Searched string
	  * @param boolean $unrestricted allows search without lang and includes first CMSCategory and exact match
	  * @return array Corresponding categories
	  */
	public static function searchByName($id_lang, $query, $unrestricted = false)
	{
		if ($unrestricted)
			return Db::getInstance()->getRow('
			SELECT c.*, cl.*
			FROM `'._DB_PREFIX_.'cms_category` c
			LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category`)
			WHERE `name` LIKE \''.pSQL($query).'\'');
		else
			return Db::getInstance()->ExecuteS('
			SELECT c.*, cl.*
			FROM `'._DB_PREFIX_.'cms_category` c
			LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = '.(int)$id_lang.')
			WHERE `name` LIKE \'%'.pSQL($query).'%\' AND c.`id_cms_category` != 1');
	}
	
	/**
	  * Retrieve CMSCategory by name and parent CMSCategory id
	  *
	  * @param integer $id_lang Language ID
	  * @param string  $CMSCategory_name Searched CMSCategory name
	  * @param integer $id_parent_CMSCategory parent CMSCategory ID
	  * @return array Corresponding CMSCategory
	  *	@deprecated
	  */
	public static function searchByNameAndParentCMSCategoryId($id_lang, $CMSCategory_name, $id_parent_CMSCategory)
	{
		Tools::displayAsDeprecated();
		return Db::getInstance()->getRow('
		SELECT c.*, cl.*
		FROM `'._DB_PREFIX_.'cms_category` c
		LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = '.(int)$id_lang.') 
		WHERE `name`  LIKE \''.pSQL($CMSCategory_name).'\' AND c.`id_cms_category` != 1 AND c.`id_parent` = '.(int)$id_parent_CMSCategory);
	}

	/**
	  * Get Each parent CMSCategory of this CMSCategory until the root CMSCategory
	  *
	  * @param integer $id_lang Language ID
	  * @return array Corresponding categories
	  */
	public function getParentsCategories($id_lang = null)
	{
		$id_lang = is_null($id_lang) ? _USER_ID_LANG_ : (int)$id_lang;

		$categories = null;
		$id_current = (int)$this->id;
		while (true)
		{
			$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
			SELECT c.*, cl.*
			FROM `'._DB_PREFIX_.'cms_category` c
			LEFT JOIN `'._DB_PREFIX_.'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = '.(int)$id_lang.')
			WHERE c.`id_cms_category` = '.(int)$id_current.' AND c.`id_parent` != 0');

			$categories[] = $result[0];
			if (!$result || $result[0]['id_parent'] == 1)
				return $categories;
			$id_current = $result[0]['id_parent'];
		}
	}
	/**
	* Specify if a CMSCategory already in base
	*
	* @param $id_cms_category CMSCategory id
	* @return boolean
	*	@deprecated
	*/
	public static function CMSCategoryExists($id_cms_category)
	{
		Tools::displayAsDeprecated();
		$row = Db::getInstance()->getRow('
		SELECT `id_cms_category`
		FROM '._DB_PREFIX_.'cms_category c
		WHERE c.`id_cms_category` = '.(int)$id_cms_category);

		return isset($row['id_cms_category']);
	}
	
	public function updatePosition($way, $position)
	{	
		if (!$res = Db::getInstance()->ExecuteS('
		SELECT cp.`id_cms_category`, cp.`position`, cp.`id_parent` 
		FROM `'._DB_PREFIX_.'cms_category` cp
		WHERE cp.`id_parent` = '.(int)$this->id_parent.' 
		ORDER BY cp.`position` ASC'))
			return false;
		foreach ($res as $category)
			if ((int)$category['id_cms_category'] == (int)$this->id)
				$movedCategory = $category;
		
		if (!isset($movedCategory) || !isset($position))
			return false;
		// < and > statements rather than BETWEEN operator
		// since BETWEEN is treated differently according to databases
		return (Db::getInstance()->Execute('
			UPDATE `'._DB_PREFIX_.'cms_category`
			SET `position`= `position` '.($way ? '- 1' : '+ 1').'
			WHERE `position` 
			'.($way 
				? '> '.(int)$movedCategory['position'].' AND `position` <= '.(int)$position
				: '< '.(int)$movedCategory['position'].' AND `position` >= '.(int)$position).'
			AND `id_parent`='.(int)$movedCategory['id_parent'])
		&& Db::getInstance()->Execute('
			UPDATE `'._DB_PREFIX_.'cms_category`
			SET `position` = '.(int)$position.'
			WHERE `id_parent` = '.(int)$movedCategory['id_parent'].'
			AND `id_cms_category`='.(int)$movedCategory['id_cms_category']));
	}
	
	public static function cleanPositions($id_category_parent)
	{
		$result = Db::getInstance()->ExecuteS('
		SELECT `id_cms_category`
		FROM `'._DB_PREFIX_.'cms_category`
		WHERE `id_parent` = '.(int)$id_category_parent.'
		ORDER BY `position`');

		$count = count($result);
		
		for ($i = 0; $i < $count; ++$i)
			Db::getInstance()->Execute('
			UPDATE `'._DB_PREFIX_.'cms_category`
			SET `position` = '.(int)$i.'
			WHERE `id_parent` = '.(int)$id_category_parent.' AND `id_cms_category` = '.(int)$result[$i]['id_cms_category']);

		return true;
	}
	
	public static function getLastPosition($id_category_parent)
	{
		return Db::getInstance()->getValue('SELECT MAX(position)+1 FROM `'._DB_PREFIX_.'cms_category` WHERE `id_parent` = '.(int)$id_category_parent);
	}

	public static function getUrlRewriteInformations($id_category)
	{
		return Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
		SELECT l.`id_lang`, c.`link_rewrite`
		FROM `'._DB_PREFIX_.'cms_category_lang` c
		LEFT JOIN  `'._DB_PREFIX_.'lang` l ON (c.`id_lang` = l.`id_lang`)
		WHERE c.`id_cms_category` = '.(int)$id_category.' AND l.`active` = 1');
	}
}