<?php

namespace App\Menu\Structure;

// TODO: [not important] nu ar trebui sa renuntam la una dintre "path" sau "id" ? - ce facem cu accesul? - sa se ia din $this->path automat, dar sa pot sa schimb dc vreau.; exceptie dc path-ul configurat de acces nu apare deloc in meniu.

class Menu extends \Engine\Menu\Structure\MenuAbstract {

	/**
	 * The path to the menu file.
	 * @var String
	 */
	protected $file;

	/**
	 * The current user object.
	 */
	protected $userAccess;

	/**
	 * Stores the singleton instantiation of the current class.
	 * @var \App\Menu\Structure\Menu
	 */
	static protected $instance;

	/**
	 * Singleton instantiation of the current class.
	 * Needed because plugins should be able to inject sections in the global menu
	 * @return \App\Menu\Structure\Menu
	 */
	static public function getInstance() {
		if (!static::$instance) {
			static::$instance=new self();
			static::$instance->loadFromXml(APP_REAL_PATH.'_config/application/menuXML2-simple.cfg.php');
		}
		return static::$instance;
	}

	/**
	 * The class constructor - called only from singleton instantiation.
	 */
	protected function __construct() {
	}

	/**
	 * Load XML file
	 * @param string $menuFile
	 * @throws \Exception
	 * @return \App\Menu\Structure\Menu
	 */
	public function loadFromXml($menuFile) {
		$this->file = $menuFile;

		// check file
		if (!is_file($menuFile)) {
			throw new \Exception("Menu file does not exist.");
		}
		$menuXmlString = include($menuFile);
		// parse and return the menu xml content
		$menuXml = simplexml_load_string($menuXmlString);
		if (!$menuXml) {
			throw new \Exception("Menu file does not contain a valid XML.");
		}

		// load menu object (recursively)
		$this->structure = $this->convertXmlToMenuObj($menuXml);

		$this->setNotificationsSupport(((String)$menuXml['hasNotifications']=='1'?true:false));

		// allow the updating of the menu from plugins
		\Engine\Plugins\Manager::getInstance()->executeEvent('Menu-afterLoad', ['menuStructure'=>$this->getStructure()]);

		return $this;
	}


	/**
	 * Recursively scans the menu XML object (attached as parameter) and adds MenuSection_General objects
	 * into the current object (references are stored in $this->children).
	 * @param \SimpleXMLElement $sectionXml - the current XML node to be scanned
	 * @param \Engine\Menu\Structure\Section $parentSection - the parent section for the sections available at this level of recursion
	 */
	protected function convertXmlToMenuObj(\SimpleXMLElement $sectionXml, ?\Engine\Menu\Structure\Section $parentSection=null) {

		// create root section
		if (!$parentSection) {
			$parentSection = new \App\Menu\Structure\Section('root', null);
		}

		// loop through all sections as they appear in XML
		foreach ($sectionXml->children() as $secXml) {
			// the current section id
			$idCurSec=(String)$secXml['id'];

			// the current access level
			$accLev=(String)$secXml['access'];
			if (!$accLev) $accLev=null;

			// get menu type
			$type = ((String)$secXml['type']?:'menuItem');

			// get js action
			$jsAction = (String)$secXml['jsAction'];

			if (!$jsAction) {
				$jsAction = null;
			}

			// creates the new MenuSection_General object with the info from the current section
			$sectionObj = new \App\Menu\Structure\Section(($idCurSec ? $idCurSec : (String)$secXml['path']), (String)$secXml['path'], $accLev);
			$sectionObj->setNonMVC((bool)$secXml['nonMVC']); // TODO: move this to app.
			$sectionObj->setType($type);
			$sectionObj->setJsAction($jsAction);

			$parentSection->addChildSection($sectionObj);

			// process the children section from the menu recursively
			if ($secXml->section) {
				$this->convertXmlToMenuObj($secXml, $sectionObj);
			}

		}

		return $parentSection;
	}

	/**
	 * Set user access
	 * @param \App\UserAccess\UserAccessAbstract $userAccess
	 * @return \App\Menu\Structure\Menu
	 */
	public function setUserAccess(\App\UserAccess\UserAccessAbstract $userAccess)
	{
		$this->userAccess = $userAccess;
		return $this;
	}

	/**
	 * Checks if the current user has access to a certain section.
	 * @param \Engine\Menu\Structure\Section $section
	 * @throws \Exception
	 */
	public function shouldDisplaySection(\Engine\Menu\Structure\Section $section) {
		// checks if the current section exists in the menu.
		if ( $this->getStructure()->getChildById($section->getId())!=$section ) {
			throw new \Exception('The current section '.$section->getId().' does not exist in the menu.');
		}

		// check if this is always accessing (eg: the home page)
		if ($section->isAlways()) {
			return true;
		}

		// separator and titles - it's like always access
		if (in_array($section->getType(), ['separator', 'title'])) {
			return true;
		}

		// check if section is inactive or hidden
		if ($section->isInactive() or $section->isHidden()) {
			return false;
		}

		// returns the access to section
		return $this->userAccess->hasAccessToSection($section->getId());
	}

	/**
	 * Get loaded menu file
	 * @return string
	 */
	public function getFile()
	{
		return $this->file;
	}

}
