<?php
/**
 * @version   $Id: AbstractRokMenuProvider.php 4585 2012-10-27 01:44:54Z btowles $
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2013 RocketTheme, LLC
 * @license   http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 only
 */

require_once(dirname(__FILE__) . '/RokMenuProvider.php');


/**
 * The base class for all data providers for menus
 */
abstract class AbstractRokMenuProvider implements RokMenuProvider
{


    /**
     * @var array
     */
    protected $args = array();

    /**
     * @var array
     */
    protected $active_branch = array();

    /**
     * @var int
     */
    protected $current_node = 0;

    /**
     * @var RokMenuNodeTree
     */
    protected $menu;

    /**
     * @param  $args
     * @return void
     */
    public function __construct(&$args)
    {
        $this->args =& $args;
    }

    /**
     * @return array
     */
    public function getActiveBranch()
    {
        return $this->active_branch;
    }

    /**
     * @return int
     */
    public function getCurrentNodeId()
    {
        return $this->current_node;
    }


    /**
     * @return RokMenuNodeTree
     */
    public function getMenuTree()
    {
        if (null == $this->menu) {
            $this->menu = new RokMenuNodeTree();
            $this->populateMenuTree();
        }
        return $this->menu;
    }

    /**
     * @return void
     */
    protected function populateMenuTree()
    {
        $nodes = $this->getMenuItems();
        $this->createMenuTree($nodes, $this->args['maxdepth']);
    }

    /**
     * Takes the menu item nodes and puts them into a tree structure
     * @param  $nodes
     * @param  $maxdepth
     * @return bool|RokMenuNodeTree
     */
    protected function createMenuTree(&$nodes, $maxdepth)
    {
        // TODO: move maxdepth to higher processing level?
        if (!empty($nodes)) {
            // Build Menu Tree root down (orphan proof - child might have lower id than parent)
            $ids = array();
            $ids[0] = true;
            $unresolved = array();

            // pop the first item until the array is empty if there is any item
            if (is_array($nodes)) {
                while (count($nodes) && !is_null($node = array_shift($nodes)))
                {
                    if (!$this->menu->addNode($node)) {
                        if (!array_key_exists($node->getId(), $unresolved) || $unresolved[$node->getId()] < $maxdepth) {
                            array_push($nodes, $node);
                            if (!isset($unresolved[$node->getId()])) $unresolved[$node->getId()] = 1;
                            else $unresolved[$node->getId()]++;
                        }
                    }
                }
            }
        }
    }

    /**
     * @param  $nodeList
     * @return void
     */
    protected function populateActiveBranch($nodeList)
    {
        // setup children array to find parents and children
        $children = array();
        $list = array();
        foreach ($nodeList as $node) {

            $thisref = &$children[$node->getId()];
            $thisref['parent_id'] = $node->getParent();
            if ($node->getParent() == 0) {
                $list[$node->getId()] = &$thisref;
            } else {
                $children[$node->getParent()]['children'][] = $node->getId();
            }
        }
        // Find active branch
        if ($this->current_node != 0) {
            if (array_key_exists($this->current_node, $nodeList)) {

                $parent_id = $children[$this->current_node]['parent_id'];
                while ($parent_id != 0) {
                    $this->active_branch[$parent_id] = $nodeList[$parent_id];
                    $parent_id = $children[$parent_id]['parent_id'];
                }
                $this->active_branch = array_reverse($this->active_branch, true);
                $this->active_branch[$this->current_node] = $nodeList[$this->current_node];
            }
        }
    }

    /**
     * This platform specific function should be implemented to get the menu nodes and return them in a RokMenuNodeTree.
     * @abstract
     * @return array of RokMenuNode objects
     */
    protected abstract function getMenuItems();
}