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

/**
 * @since  v4.0
 * @author Christoph
 */
class _WV24_Generator {
	protected $cache;

	public function __construct(_WV24_RuntimeCache $cache) {
		$this->cache = $cache;
	}

	/**
	 * @return _WV24_RuntimeCache
	 */
	public function getCache() {
		return $this->cache;
	}

	public function buildUrlForArticle(sly_Model_Article $article) {
		return $this->buildUrl($article->getId(), $article->getClang());
	}

	public function buildUrl($articleID, $clang = null) {
		$articleID = (int) $articleID;
		$clang     = $clang === null ? sly_Core::getCurrentClang() : (int) $clang;

		// make sure we never use a cached URL when we're supposed to build it
		$this->cache->forgetUrl($articleID, $clang);

		return $this->_buildUrl($articleID, $clang);
	}

	protected function _buildUrl($articleID, $clang) {
		// Did we generate this URL already?
		if ($this->cache->hasUrl($articleID, $clang)) {
			return $this->cache->getUrl($articleID, $clang);
		}

		// The site start article requires some special attention.
		if ($articleID === sly_Core::getSiteStartArticleId()) {
			return $this->buildSiteStartUrl($clang);
		}

		// collect some info about the article and prepare the article's fragment
		$article  = $this->cache->getArticleData($articleID, $clang);
		$fragment = $this->prepareFragment($article);

		// and finally build the URL for the fragment
		$fullUrl = $this->buildUrlForFragment($article, $fragment);

		// Since the above function does the caching already, we're done here.
		return $fullUrl;
	}

	public function buildUrlForFragment(array $article, $fragment) {
		$fullUrl = null;

		// Domain fragments start a fresh URL and do not inherit their parent's url.
		if (WV24_Util::isDomainFragment($fragment)) {
			$fullUrl = 'http:'.$fragment;

			// In case the fragment does not contain a path (looks like "//domain"),
			// we have to add the root-level directory ourselves. Otherwise, leave
			// it alone.
			if (substr_count($fragment, '/') === 2) {
				$fullUrl .= '/';
			}
		}

		// Absolute fragments also start a new tree, but are based on their nearest
		// parent's domain (if given, else the default fragment is used).
		elseif (WV24_Util::isAbsoluteFragment($fragment)) {
			$path      = $article['path'];
			$parentID  = $article['re_id'];
			$clang     = $article['clang'];
			$parentUrl = null;
			$pathInfo  = $this->cache->getPathInfo($path, $clang);

			// find the nearest domain fragment
			while ($parentID !== 0) {
				$parentFragment = $pathInfo[$parentID]['fragment'];

				if (mb_strlen($parentFragment) !== 0 && WV24_Util::isDomainFragment($parentFragment)) {
					$parentUrl = $this->_buildUrl($parentID, $clang);
					break;
				}

				$parentID = $pathInfo[$parentID]['re_id'];
			}

			// if nothing was found, we use the default one
			if ($parentUrl === null) {
				$parentUrl = $this->buildSiteStartUrl($clang);
			}

			$fullUrl = rtrim($parentUrl, '/').$fragment;
		}

		// regular fragments or no fragment given
		else {
			$path     = $article['path'];
			$parentID = $article['re_id'];
			$clang    = $article['clang'];
			$pathInfo = $this->cache->getPathInfo($path, $clang);

			// skip all hidden categories until we find the first visible one
			while ($parentID !== 0 && $pathInfo[$parentID]['skipped'] && !WV24_Util::isDomainFragment($pathInfo[$parentID]['fragment'])) {
				$parentID = $pathInfo[$parentID]['re_id'];
			}

			// If the parent is the virtual root category (i.e. we're at the top
			// of the structure), we use the default fragment to start the URL.
			// Since this is the same as using the site start article's URL, we
			// just re-declare the parent.
			if ($parentID === 0) {
				$parentID = sly_Core::getSiteStartArticleId();

				if (!sly_Util_Article::exists($parentID)) {
					throw new Exception('Der Seiten-Startartikel (ID '.$parentID.') wurde nicht gefunden.');
				}
			}

			// build the parent URL (no-op if already done)
			$parentUrl = $this->_buildUrl($parentID, $clang);
			$fullUrl   = rtrim($parentUrl, '/').'/'.$fragment;
		}

		// cache the full URL for later use
		$this->cache->addUrl($article['id'], $article['clang'], $fullUrl);

		return $fullUrl;
	}

	public function buildSiteStartUrl($clang) {
		$clang   = $clang === null ? sly_Core::getCurrentClang() : (int) $clang;
		$article = sly_Core::getSiteStartArticleId();

		if (!sly_Util_Article::exists($article)) {
			throw new Exception('Der Seiten-Startartikel (ID '.$article.') wurde nicht gefunden.');
		}

		// Did we generate this URL already?
		if ($this->cache->hasUrl($article, $clang)) {
			return $this->cache->getUrl($article, $clang);
		}

		// This is easy, just use the configured domain fragment, no matter what
		// it looks like (e.g. '//domain' or '//domain/foo' or '//domain/francais/').
		$url = 'http:'.rtrim($this->cache->getDefaultFragment($clang), '/').'/';

		$this->cache->addUrl($article, $clang, $url);
		return $url;
	}

	protected function prepareFragment(array $article) {
		$fragment          = $article['fragment'];
		$hasCustomFragment = mb_strlen($fragment) > 0;

		// if no fragment is set, build the fragment by using the article's name
		if (!$hasCustomFragment) {
			$fragment = $this->buildSlug($article);
		}
		elseif ($fragment === '/') {
			return '/';
		}

		$fragment = rtrim($fragment, '/');

		// append the typical suffix the the fragment
		if (!$hasCustomFragment) {
			$fragment .= $article['is_cat'] ? '/' : '.html';
		}

		return $fragment;
	}

	protected function buildSlug(array $article) {
		$name      = $article['name'];
		$clang     = $article['clang'];
		$cleanup   = $this->cache->getSetting('cleanup', $clang);
		$lowercase = $this->cache->getSetting('lowercase', $clang);
		$slug      = WV24_Util::makeURLFriendly($name, $cleanup, $lowercase);

		// return a sane default value to avoid URLs like '/cat/.html'
		if (mb_strlen($slug) === 0) {
			return $article['is_cat'] ? 'unbenannte-kategorie' : 'unbenannter-artikel';
		}

		return $slug;
	}
}
