<?php
/**
 * sh404SEF - SEO extension for Joomla!
 *
 * @author      Yannick Gaultier
 * @copyright   (c) Yannick Gaultier 2014
 * @package     sh404SEF
 * @license     http://www.gnu.org/copyleft/gpl.html GNU/GPL
 * @version     4.4.4.1791
 * @date		2014-07-01
 */

// Security check to ensure this file is being included by a parent file.
if (!defined('_JEXEC'))
	die('Direct Access to this location is not allowed.');

class Sh404sefModelSlugs
{

	private static $_instance = null;
	private static $_articles = array();
	private static $_categories = array();

	/**
	 * Singleton method
	 *
	 * @param string $extension extension name, with com_ - ie com_content
	 * @return object instance of Sh404sefModelCategories
	 */
	public static function getInstance()
	{

		if (is_null(self::$_instance))
		{
			self::$_instance = new Sh404sefModelSlugs();
		}

		return self::$_instance;
	}

	public function getArticle($id)
	{

		// sanitize input
		$id = intval($id);
		if (empty($id))
		{
			throw new Sh404sefExceptionDefault('Invalid article id passed to ' . __METHOD__ . ' in ' . __CLASS__, 500);
		}

		// already cached ?
		if (empty(self::$_articles[$id]))
		{
			// read details about the article
			$article = ShlDbHelper::selectObject('#__content', array('id', 'title', 'alias', 'catid', 'language'), array('id' => $id));

			// if not found, that's bad
			if (empty($article))
			{
				throw new Sh404sefExceptionDefault('Non existing article id (' . $id . ') passed to ' . __METHOD__ . ' in ' . __CLASS__, 500);
			}

			// store our cached record
			self::$_articles[$id][$article->language] = $article;
		}

		return self::$_articles[$id];
	}

	public function getArticleSlug($id, $useAlias, $insertId, $insertIdCatList, $requestedLanguage = '*', $separator = '')
	{

		$rawArticle = $this->getArticle($id);

		// select language
		$language = $requestedLanguage;
		if (empty($rawArticle[$language]))
		{
			$language = '*';
		}
		// still no luck, use whatever is available
		if (empty($rawArticle[$language]))
		{
			$languages = array_keys($rawArticle);
			$language = array_shift($languages);
		}

		// must insert id ?
		$insertId = $this->_shouldInsertArticleId($insertId, $insertIdCatList, $rawArticle[$language]->catid);

		// build slug now
		$slug = '';
		if ($insertId)
		{
			$separator = empty($separator) ? Sh404sefFactory::getConfig()->replacement : $separator;
			$slug = $id . $separator;
		}
		$slug .= $useAlias ? $rawArticle[$language]->alias : $rawArticle[$language]->title;

		return $slug;
	}

	protected function _shouldInsertArticleId($insertId, $insertIdCatList, $catId)
	{

		if (empty($insertId) || empty($catId))
		{
			return false;
		}

		// we should insert id if article category is listed in the parameter
		$shouldInsert = (!empty($insertIdCatList) && empty($insertIdCatList[0])) || in_array($catId, $insertIdCatList);

		return $shouldInsert;
	}

	/**
	 *
	 * Get an object describing a category, for the
	 * purpose of sh404SEF usage, for a Joomla! category
	 *
	 * @param string $extension extension for which category is searched
	 * @param integer $id id of requested category
	 * @throws Sh404sefExceptionDefault if invalid id
	 */
	public function getCategory($extension, $id)
	{

		// sanitize input
		$extension = strtolower($extension);
		if (empty($extension) || substr($extension, 0, 4) !== 'com_')
		{
			throw new Sh404sefExceptionDefault('Invalid extension (' . $extension . ') passed to ' . __METHOD__ . ' in ' . __CLASS__, 500);
		}
		$id = intval($id);
		if (empty($id))
		{
			throw new Sh404sefExceptionDefault('Invalid category id passed to ' . __METHOD__ . ' in ' . __CLASS__, 500);
		}

		// check if cached, create if not
		if (empty(self::$_categories[$extension]) || empty(self::$_categories[$extension][$id]))
		{

			// get the Joomla! built category node
			jimport('joomla.application.categories');
			$options = array('access' => false, 'published' => 0);
			$categories = JCategories::getInstance(str_replace('com_', '', $extension), $options);

			// and ask for the category Joomla! object
			$node = $categories->get($id);

			// no data? error
			if (empty($node))
			{
				throw new Sh404sefExceptionDefault('Non existing category id (' . $id . ') passed to ' . __METHOD__ . ' in ' . __CLASS__, 500);
			}

			// we have an object, build our record
			$cat = new StdClass();
			$cat->id = $node->id;
			$cat->extension = $node->extension;
			$cat->title = $node->title;
			$cat->alias = $node->alias;
			$cat->language = $node->language;
			$cat->params = $node->params;
			$cat->metadesc = $node->metadesc;
			$cat->metakey = $node->metakey;
			$cat->metadata = $node->metadata;
			$cat->pathArray = $this->_buildCategoryRawNodePathArray($node);

			self::$_categories[$extension][$id][$node->language] = $cat;
		}

		// do we now have the category? if not, throw Exception
		if (empty(self::$_categories[$extension]) || empty(self::$_categories[$extension][$id]))
		{
			throw new Sh404sefExceptionDefault('Non existing category id (' . $id . ') passed to ' . __METHOD__ . ' in ' . __CLASS__, 500);
		}

		return self::$_categories[$extension][$id];

	}

	public function getCategoryPathArray($extension, $id, $whichCat, $useAlias, $insertId, $requestedLanguage = '*', $separator = '')
	{

		// get full category data
		$rawCat = $this->getCategory($extension, $id);

		// select language
		$language = $requestedLanguage;
		if (empty($rawCat[$language]))
		{
			$language = '*';
		}

		// still no luck, use whatever is available
		if (empty($rawCat[$language]))
		{
			$languages = array_keys($rawCat);
			$language = array_shift($languages);
		}

		// break reference
		if (!empty($rawCat[$language]))
		{
			$copyCat = clone ($rawCat[$language]);
		}
		else
		{
			throw new Sh404sefExceptionDefault('Language (' . $requestedLanguage . ') not found in categories list, ' . __METHOD__ . ' in '
					. __CLASS__, 500);
		}

		// only keep appropriate parts, according to request
		switch ($whichCat)
		{
			case shSEFConfig::CAT_ALL_NESTED_CAT:
				$pathArray = $copyCat->pathArray;
				break;
			case shSEFConfig::CAT_NONE:
				$pathArray = array();
				break;
			case shSEFConfig::CAT_FIRST:
				$pathArray = array(array_shift($copyCat->pathArray));
				break;
			case shSEFConfig::CAT_LAST:
				$pathArray = array(array_pop($copyCat->pathArray));
				break;
			case shSEFConfig::CAT_2_FIRST:
				$pathArray = $copyCat->pathArray;
				while (count($pathArray) > 2)
				{
					array_pop($pathArray);
				}
				break;
			case shSEFConfig::CAT_2_LAST:
				$pathArray = $copyCat->pathArray;
				while (count($pathArray) > 2)
				{
					array_shift($pathArray);
				}
				break;
			default:
				throw new Sh404sefExceptionDefault('Invalid configuration option (' . print_r($id) . ') passed to ' . __METHOD__ . ' in ' . __CLASS__,
					500);
				;
				break;
		}
		// build slug, according to request
		foreach ($pathArray as $key => $value)
		{
			$pathArray[$key]->slug = $useAlias ? $pathArray[$key]->alias : $pathArray[$key]->title;
			if ($insertId)
			{
				$separator = empty($separator) ? Sh404sefFactory::getConfig()->replacement : $separator;
				$pathArray[$key]->slug = $pathArray[$key]->id . $separator . $pathArray[$key]->slug;
			}
		}

		// return formatted Path
		return empty($pathArray) ? array() : $pathArray;
	}

	public function getCategorySlugArray($extension, $id, $whichCat, $useAlias, $insertId, $uncategorizedPath = '', $requestedLanguage = '*',
		$separator = '')
	{
		// special case for the "uncategorised" category
		$unCat = Sh404sefHelperCategories::getUncategorizedCat($extension);
		if (!empty($unCat) && $id == $unCat->id)
		{
			$slug = $useAlias ? $unCat->title : $unCat->alias;
			$slugArray = empty($uncategorizedPath) ? array($slug) : array($uncategorizedPath, $slug);
			return $slugArray;
		}

		// regular category, build the path to the cat
		$separator = empty($separator) ? Sh404sefFactory::getConfig()->replacement : $separator;
		$pathArray = $this->getCategoryPathArray($extension, $id, $whichCat, $useAlias, $insertId, $requestedLanguage, $separator);

		$slugArray = array();
		foreach ($pathArray as $catObject)
		{
			$slugArray[] = $catObject->slug;
		}

		return $slugArray;
	}

	/**
	 *
	 * Build an array holding the various path items
	 * for a given category, as descrobed by a JCategoryNode object
	 * complying with general SEF url generation parameters
	 *
	 * @param JCategoryNode object $node the category node
	 */
	private function _buildCategoryRawNodePathArray($node)
	{
		// holds result
		$pathArray = array();

		// iterate over node parent cats
		$safer = 0;
		do
		{
			$tmp = new stdClass();
			$tmp->id = $node->id;
			$tmp->title = $node->title;
			$tmp->alias = $node->alias;
			$tmp->slug = '';
			$pathArray[] = $tmp;
			$node = $node->getParent();
			$isRoot = empty($node) || $node->id == 'root';
			$safer++;
		}
		while (!$isRoot && $safer < 20);

		// get first things first
		$pathArray = array_reverse($pathArray);

		// process rules for building the category url path

		// return final array
		return $pathArray;
	}

}
