<?php

class pdoMenu {
	/** @var modX $modx */
	public $modx;
	/** @var  pdoFetch|pdoTools $pdoTools  */
	public $pdoTools;
	/** @var array $tree */
	protected $tree = array();
	/** @var array $parentTree */
	protected $parentTree = array();
	/** @var int $level */
	protected $level = 1;


	/**
	 * @param modX $modx
	 * @param array $config
	 */
	public function __construct(modX & $modx, $config = array()) {
		$this->modx = &$modx;

		$config = array_merge(
			array(
				'firstClass' => 'first',
				'lastClass' => 'last',
				'hereClass' => 'active',
				'parentClass' => '',
				'rowClass' => '',
				'outerClass' => '',
				'innerClass' => '',
				'levelClass' => '',
				'selfClass' => '',
				'webLinkClass' => '',
				'limit' => 0,
				'hereId' => 0,
			),
			$config,
			array(
				'return' => 'data',
			)
		);

		if (empty($config['tplInner']) && !empty($config['tplOuter'])) {
			$config['tplInner'] = $config['tplOuter'];
		}
		if (empty($config['hereId']) && !empty($modx->resource)) {
			$config['hereId'] = $modx->resource->id;
		}

		$fqn = $modx->getOption('pdoFetch.class', null, 'pdotools.pdofetch', true);
		if ($pdoClass = $modx->loadClass($fqn, '', false, true)) {
			$this->pdoTools = new $pdoClass($modx, $config);
		}
		elseif ($pdoClass = $modx->loadClass($fqn, MODX_CORE_PATH . 'components/pdotools/model/', false, true)) {
			$this->pdoTools = new $pdoClass($modx, $config);
		}
		else {
			$this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not load pdoFetch from "MODX_CORE_PATH/components/pdotools/model/".');
			return false;
		}

		if ($config['hereId'] && $currentResource = $this->pdoTools->getObject('modResource', $config['hereId'])) {
			$tmp = $modx->getParentIds($currentResource['id'], 100, array('context' => $currentResource['context_key']));
			$tmp[] = $config['hereId'];
			$this->parentTree = array_flip($tmp);
		}

		$modx->lexicon->load('pdotools:pdomenu');
		return true;
	}


	/**
	 * Gets tree of resources and template it
	 *
	 * @param array $tree
	 *
	 * @return mixed
	 */
	public function templateTree($tree = array()) {
		$this->tree = $tree;
		$count = count($tree);
		$output = '';

		$idx = 1;
		$this->pdoTools->addTime('Start template tree');
		foreach ($tree as $row) {
			if (empty($row['id'])) {continue;}
			$this->level = 1;
			$row['idx'] = $idx++;
			$row['last'] = (integer) $row['idx'] == $count;

			$output .= $this->templateBranch($row);
		}
		$this->pdoTools->addTime('End template tree');

		if (!empty($output)) {
			$pls = $this->addWayFinderPlaceholders(
				array(
					'wrapper' => $output,
					'classes' => ' class="'.$this->pdoTools->config['outerClass'].'"',
					'classNames' => $this->pdoTools->config['outerClass'],
					'classnames' => $this->pdoTools->config['outerClass'],
					'level' => $this->level,
				)
			);
			$output = $this->pdoTools->parseChunk($this->pdoTools->config['tplOuter'], $pls);
		}

		return $output;
	}


	/**
	 * Recursive template of branch of menu
	 *
	 * @param array $row
	 *
	 * @return mixed|string
	 */
	public function templateBranch($row = array()) {
		$children = '';
		$row['level'] = $this->level;

		if (!empty($row['children']) && ($this->isHere($row['id']) || empty($this->pdoTools->config['hideSubMenus'])) && $this->checkResource($row['id'])) {
			$idx = 1;
			$this->level++;
			$count = count($row['children']);
			foreach ($row['children'] as $v) {
				$v['idx'] = $idx++;
				$v['last'] = (integer) $v['idx'] == $count;

				$children .= $this->templateBranch($v);
			}
			$this->level--;
			$row['children'] = $count;
		}
		else {
			$row['children'] = isset($row['children']) ? count($row['children']) : 0;
		}

		if (!empty($this->pdoTools->config['countChildren'])) {
			if ($ids = $this->modx->getChildIds($row['id'])) {
				$tstart = microtime(true);
				$count = $this->modx->getCount('modResource', array(
					'id:IN' => $ids,
					'published' => true,
					'deleted' => false,
				));
				$this->modx->queryTime += microtime(true) - $tstart;
				$this->modx->executedQueries++;
				$this->pdoTools->addTime('Got the number of active children for resource "'.$row['id'].'": ' . $count);
			}
			else {
				$count = 0;
			}
			$row['children'] = $count;
		}

		if (!empty($children)) {
			$pls = $this->addWayFinderPlaceholders(array(
				'wrapper' => $children,
				'classes' => ' class="'.$this->pdoTools->config['innerClass'].'"',
				'classNames' => $this->pdoTools->config['innerClass'],
				'classnames' => $this->pdoTools->config['innerClass'],
				'level' => $this->level,
			));
			$row['wrapper'] = $this->pdoTools->parseChunk($this->pdoTools->config['tplInner'], $pls);
		}
		else {
			$row['wrapper'] = '';
		}

		if (empty($row['menutitle']) && !empty($row['pagetitle'])) {
			$row['menutitle'] = $row['pagetitle'];
		}

		$classes = $this->getClasses($row);
		if (!empty($classes)) {
			$row['classNames'] = $row['classnames'] = $classes;
			$row['classes'] = ' class="'.$classes.'"';
		}
		else {
			$row['classNames'] = $row['classnames'] = $row['classes'] = '';
		}

		if (!empty($this->pdoTools->config['useWeblinkUrl']) && $row['class_key'] == 'modWebLink') {
			$row['link'] = is_numeric(trim($row['content'], '[]~ '))
				? $this->modx->makeUrl(intval(trim($row['content'], '[]~ ')), '', '', $this->pdoTools->config['scheme'])
				: $row['content'];
		}
		else {
			$row['link'] = $this->modx->makeUrl($row['id'], $row['context_key'], '', $this->pdoTools->config['scheme']);
		}

		$row['title'] = !empty($this->pdoTools->config['titleOfLinks'])
			? $row[$this->pdoTools->config['titleOfLinks']]
			: '';

		$tpl = $this->getTpl($row);
		$row = $this->addWayFinderPlaceholders($row);

		return $this->pdoTools->getChunk($tpl, $row, $this->pdoTools->config['fastMode']);
	}


	/**
	 * Determine the "you are here" point in the menu
	 *
	 * @param int $id
	 *
	 * @return bool
	 */
	public function isHere($id = 0) {
		return isset($this->parentTree[$id]);
	}


	/**
	 * Determine style class for current item being processed
	 *
	 * @param array $row Array with resource properties
	 *
	 * @return string
	 */
	public function getClasses($row = array()) {
		$classes = array();

		if (!empty($this->pdoTools->config['rowClass'])) {
			$classes[] = $this->pdoTools->config['rowClass'];
		}
		if ($row['idx'] == 1 && !empty($this->pdoTools->config['firstClass'])) {
			$classes[] = $this->pdoTools->config['firstClass'];
		}
		elseif (!empty($row['last']) && !empty($this->pdoTools->config['lastClass'])) {
			$classes[] = $this->pdoTools->config['lastClass'];
		}
		if (!empty($this->pdoTools->config['levelClass'])) {
			$classes[] = $this->pdoTools->config['levelClass'] . $row['level'];
		}
		if ($row['children'] && !empty($this->pdoTools->config['parentClass']) && ($row['level'] < $this->pdoTools->config['level'] || empty($this->pdoTools->config['level']))) {
			$classes[] = $this->pdoTools->config['parentClass'];
		}
		if ($this->isHere($row['id']) && !empty($this->pdoTools->config['hereClass'])) {
			$classes[] = $this->pdoTools->config['hereClass'];
		}
		if ($row['id'] == $this->pdoTools->config['hereId'] && !empty($this->pdoTools->config['selfClass'])) {
			$classes[] = $this->pdoTools->config['selfClass'];
		}
		if (!empty($row['class_key']) && $row['class_key'] == 'modWebLink' && !empty($this->pdoTools->config['weblinkClass'])) {
			$classes[] = $this->pdoTools->config['weblinkClass'];
		}

		return implode(' ', $classes);
	}


	/**
	 * Determine style class for current item being processed
	 *
	 * @param array $row
	 *
	 * @return mixed
	 */
	public function getTpl($row = array()) {
		if ($row['level'] == 1 && !empty($this->pdoTools->config['tplStart']) && !empty($this->pdoTools->config['displayStart'])) {
			$tpl = 'tplStart';
		}
		elseif ($row['children'] && $row['id'] == $this->pdoTools->config['hereId'] && !empty($this->pdoTools->config['tplParentRowHere'])) {
			$tpl = 'tplParentRowHere';
		}
		elseif ($row['level'] > 1 && $row['id'] == $this->pdoTools->config['hereId'] && !empty($this->pdoTools->config['tplInnerHere'])) {
			$tpl = 'tplInnerHere';
		}
		elseif ($row['id'] == $this->pdoTools->config['hereId'] && !empty($this->pdoTools->config['tplHere'])) {
			$tpl = 'tplHere';
		}
		elseif ($row['children'] && $this->isHere($row['id']) && !empty($this->pdoTools->config['tplParentRowActive'])) {
			$tpl = 'tplParentRowActive';
		}
		elseif ($row['children'] && (empty($row['template']) || strpos($row['link_attributes'], 'category') != false) && !empty($this->pdoTools->config['tplCategoryFolder'])) {
			$tpl = 'tplCategoryFolder';
		}
		// It's a typo, but it is left for backward compatibility
		elseif ($row['children'] && (empty($row['template']) || strpos($row['link_attributes'], 'category') != false) && !empty($this->pdoTools->config['tplCategoryFolders'])) {
			$tpl = 'tplCategoryFolders';
		}
		// ---
		elseif ($row['children'] && !empty($this->pdoTools->config['tplParentRow'])) {
			$tpl = 'tplParentRow';
		}
		elseif ($row['level'] > 1 && !empty($this->pdoTools->config['tplInnerRow'])) {
			$tpl = 'tplInnerRow';
		}
		else {
			$tpl = 'tpl';
		}

		return $this->pdoTools->config[$tpl];
	}


	/**
	 * This method adds special placeholders for compatibility with Wayfinder
	 *
	 * @param array $row
	 *
	 * @return array
	 */
	public function addWayFinderPlaceholders($row = array()) {
		$pl = $this->pdoTools->config['plPrefix'];
		foreach ($row as $k => $v) {
			switch ($k) {
				case 'id':
					if (!empty($this->pdoTools->config['rowIdPrefix'])) {
						$row[$pl.'id'] = ' id="'.$this->pdoTools->config['rowIdPrefix'].$v.'"';
					}
					$row[$pl.'docid'] = $v;
					break;
				case 'menutitle':
					$row[$pl.'linktext'] = $v;
					$row[$pl.'menutitle'] = $v;
					break;
				case 'link_attributes':
					$row[$pl.'attributes'] = $v;
					$row['attributes'] = $v;
					break;
				case 'children':
					$row[$pl.'subitemcount'] = $v;
					break;
				default:
					$row[$pl.$k] = $v;
			}
		}

		return $row;
	}


	/**
	 * Verification of resource status
	 *
	 * @param int $id
	 *
	 * @return bool|int
	 */
	public function checkResource($id = 0) {
		$tmp = array();
		if (empty($this->pdoTools->config['showHidden'])) {
			$tmp['hidemenu'] = 0;
		}
		if (empty($this->pdoTools->config['showUnpublished'])) {
			$tmp['published'] = 1;
		}
		if (!empty($this->pdoTools->config['hideUnsearchable'])) {
			$tmp['searchable'] = 1;
		}

		if (!empty($tmp)) {
			$tmp['id'] = $id;
			$q = $this->modx->newQuery('modResource', $tmp);
			$q->select('id');

			$tstart = microtime(true);
			if ($q->prepare() && $q->stmt->execute()) {
				$this->modx->queryTime += microtime(true) - $tstart;
				$this->modx->executedQueries++;
				$res = $q->stmt->fetch(PDO::FETCH_COLUMN);
				return (boolean) $res;
			}
		}

		return true;
	}

}
