<?php
// last edit: 22.06.2018

/*
	Данный модуль был разработан для удобного формирования меню, которое можно легко выводить в шаблоне.
	Меню имеет древовидную структуру, и элементы можно перебирать с помощью foreach

	<submenu_params>
		[<id> => <name>,...]
		[<id> => [<::create_param_$arr>],...]
		[object(MenuItem),...]
		[<id>,...]
		[[<id>,[<::create_param_$arr>]],...]

	<::create_param_$arr>
		[[<submenu_params>,...]]
		[<name>]
		[<name>, [<submenu_params>,...]]
		[[<icon_string>,<name>], [<submenu_params>,...]]
		[<name>, <url>]
		[[<icon_string>,<name>], <url>]
		[<name>, <url>, [<submenu_params>,...]]
		[[<icon_string>,<name>], <url>, [<submenu_params>,...]]

*/

class MenuItem implements Iterator{
	private $name, $url, $id, $icon, $submenu = [];

	/**
	* Создание нового пункта меню
	*
	* @param string|array $id - ID меню, по которому бедет осуществляться поиск данного пункта
	* @param array $arr - массив параметров создания, в него можно вложить как настройки только текущего пункта, так и вложенных в него подпунктов (вложенного меню)
	* 	[[<submenu_params>,...]]
	*   [<name>]
	*   [<name>, [<submenu_params>,...]]
	*   [[<icon_string>,<name>], [<submenu_params>,...]]
	*   [<name>, <url>]
	*   [[<icon_string>,<name>], <url>]
	*   [<name>, <url>, [<submenu_params>,...]]
	*   [[<icon_string>,<name>], <url>, [<submenu_params>,...]]
	* @return object(MenuItem)
	*/
	static function create($id, $arr = []){
		switch(count($arr)){
			case 0: return new MenuItem($id, $id); break;
			case 1: 
				if(is_array($arr[0]))
					return (new MenuItem($id, $id))->SubMenu($arr[0]);
				else 
					return new MenuItem($id, $arr[0]);
				break;
			case 2:
				if(is_array($arr[1]))
					return (new MenuItem($id,$arr[0]))->SubMenu($arr[1]);
				else 
					return new MenuItem($id,$arr[0],$arr[1]);
				break;
			case 3: return (new MenuItem($id,$arr[0],$arr[1]))->SubMenu($arr[2]); break;
		}
	}

	/**
	* Конструктор пункта меню
	*
	* @param string $id         - ID пункта меню
	* @param string $name       - Имя выводимое на экран
	*	string
	*	[<icon_string>,<name>]
	* @param string|false $url  - ссылка в которую оборачивается пункт меню
	*/
	function __construct($id, $name, $url = false){
		$this->id = $id;
		if(is_array($name)){
			$this->icon = $name[0];
			$this->name = $name[1];
		} else {
			$this->name = $name;
			$this->icon = false;
		}
		$this->url = $url;
	}

	/**
	* Удаление пункта меню, по его ID (поиск по всему дереву, от текущего элемента и вниз)
	*
	* @param string $id  - ID пункта меню, который необходимо удалить
	* @return $this
	*/
	function deleteId($id){
		$this->submenu = array_filter($this->submenu, function($menuItem)use($id){
			return !$menuItem->deleteId($id)->isId($id);
		});
		return $this;
	}

	/**
	* Добавление текста к имени меню, указанного по ID (поиск по всему дереву, от текущего элемента и вниз)
	*
	* @param string $id   - ID пункта меню, к которому надо добавить текст
	* @param string $text - текст который необходимо добавить в конец пункта меню
	* @return $this
	*/
	function appendForId($id, $text){
		foreach ($this as $itemMenu) {
			if($itemMenu->isId($id))
				$itemMenu->appendToName($text);
		}
		return $this;
	}

	/**
	* Получить имя меню
	* 
	* @return string
	*/
	function getName(){
		return $this->name;
	}

	/**
	* Добавить к имени меню текст
	*
	* @param string $text - текст который необходимо добавить в конец пункта меню
	* @return $this
	*/
	function appendToName($text){
		$this->name .= $text;
		return $this;
	}

	/**
	* Получение ссылки меню
	*
	* @return string|false
	*/
	function getUrl(){
		return $this->url;
	}

	/**
	* Получить вложенное меню, в текущее
	* 
	* @return $this
	*/
	function getSubMenu(){
		return $this->submenu;
	}

	/**
	* Получить закрепленную к этому пункту иконку 
	* 
	* @return string|false
	*/
	function getIcon(){
		return $this->icon;
	}

	/**
	* Получить ID пункта
	*
	* @return string
	*/
	function getId(){
		return $this->id;
	}

	/**
	* Проверяет является ли текущий пункт меню искомым
	* 
	* @param string $id  - искомый ID пункта меню
	* @return bool
	*/
	function isId($id){
		return $this->id == $id;
	}

	/**
	* Добавляет в подменю, переданный пункт меню
	*
	* @param object(MenuItem) $MenuItem  - переданный пункт меню, который необходимо добавить в подменю
	* @return $this
	*/
	function addSubMenuItem($MenuItem){
		$this->submenu[] = $MenuItem;
		return $this;
	}

	/**
	* Создает пункт меню, в подменю
	*
	* @param string $id         - ID пункта меню
	* @param string $name       - Имя выводимое на экран
	*	string
	*	[<icon_string>,<name>]
	* @param string|false $url  - ссылка в которую оборачивается пункт меню
	* @return object(MenuItem)
	*/
	function createSubMenuItem($id, $name, $url = false){
		$item = new MenuItem($id, $name, $url);
		$this->submenu[] = $item;
		return $item;
	}

	/**
	* Проверяет, существует ли у пункта подменю
	*
	* @return bool
	*/
	function isSubMenu(){
		return count($this->submenu) > 0;
	}

	/**
	* Преобразовывает передаваемую структуру (<submenu_params>), в подменю
	*
	*	[<id> => <name>,...]
	*	[<id> => [<::create_param_$arr>],...]
	*	[object(MenuItem),...]
	*	[<id>,...]
	*	[[<id>,[<::create_param_$arr>]],...]
	*
	* @param array $MenuItems  - древовидный массив, или структура меню
	* @return $this
	*/
	function SubMenu($MenuItems){
		foreach ($MenuItems as $key => $value)
			if(is_numeric($key)){
				if(is_array($value)){
					$id = array_shift($value);
					$this->addSubMenuItem( MenuItem::create($id, $value) );
				} elseif(is_object($value)){
					$this->addSubMenuItem($value);
				} else $this->createSubMenuItem($value, $value);
			} elseif(is_array($value))
				$this->addSubMenuItem( MenuItem::create($key, $value) );
			else $this->createSubMenuItem($key, $value);

		return $this;
	}

	/* Iterator */

	function rewind(){
        reset($this->submenu);
    }

    function current(){
        return current($this->submenu);
    }

    function key(){
        return key($this->submenu);
    }

    function next(){
        return next($this->submenu);
    }

	function valid(){
        $key = key($this->submenu);
        return ($key !== NULL && $key !== FALSE);
    }

    function isEnd($menuItem){
    	$ct = count($this->submenu)-1;
    	return $ct >= 0 && $this->submenu[$ct] == $menuItem;
    }

}