<?php
/**

* EfrontTree Class file

*

* @package eFront

*/
//This file cannot be called directly, only included.
if (str_replace(DIRECTORY_SEPARATOR, "/", __FILE__) == $_SERVER['SCRIPT_FILENAME']) {
    exit;
}
/**

 * Efront tree exceptions

 *

 * @package eFront

 */
class EfrontTreeException extends Exception
{
    const NODE_NOT_EXISTS = 1001;
}
/**

 * Tree class

 *

 * @package eFront

 */
abstract class EfrontTree
{
    /**

     * The tree object.

     *

     * @since 3.5.0

     * @var RecursiveArrayIterator

     * @access public

     */
    public $tree;
    /**

     * Get the first tree node

     *

     * This function returns the array corresponding to the first node in the tree.

     * <br/>Example:

     * <code>

     * $content = new EfrontContentTree(4);					//Create the content tree for lesson with id 4

     * $firstNode = $content -> getFirstNode();				//$firstNode now holds the array of the tree's first node

     * </code>

     *

     * @param ArrayIterator $iterator An specific iterator to use, instead of the default one

     * @return array The first node array

     * @since 3.5.0

     * @access public

     */
 public function getFirstNode($iterator = false) {
       if (!$iterator) {
           $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create a new iterator, so that the internal iterator pointer is not reset
       }
       $iterator -> rewind(); //Initialize iterator
       return $iterator -> current();
    }
    /**

     * Get the last tree node

     *

     * This function returns the array corresponding to the last node in the tree.

     * It does not alter the inner tree pointer

     * <br/>Example:

     * <code>

     * $content = new EfrontContentTree(4);					//Create the content tree for lesson with id 4

     * $lastNode = $content -> getLastNode();				//$lastNode now holds the array of the content tree's last node

     * </code>

     *

     * @param ArrayIterator $iterator An specific iterator to use, instead of the default one

     * @return array The last node array

     * @since 3.5.0

     * @access public

     */
 public function getLastNode($iterator = false) {
       if (!$iterator) {
           $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST));
       }
       foreach ($iterator as $lastNode); //We create new iterators (in order to leave unchanged the internal tree pointer) and advance them to the end of the tree
       return $lastNode;
    }
    /**

     * Get the next node in the tree

     *

     * The function returns the specified node's next node

     * <br/>Example:

     * <code>

     * $content = new EfrontContentTree(4);				//Create the content tree for lesson with id 4

     * $node = $content -> getFirstNode();				//$node now holds the array of the content tree's first node

     * $node = $content -> getNextNode(32);				//$node now holds the array of node's 32 next node

     * </code>

     * if the specified node is the last one, false is returned

     *

     * @param int $queryNode A node id, to get its next node

     * @param ArrayIterator $iterator An specific iterator to use, instead of the default one

     * @return array The next node array

     * @since 3.5.0

     * @access public

     */
    public function getNextNode($queryNode, $iterator = false) {
        $queryNode instanceof ArrayObject ? $nodeId = $queryNode['id'] : $nodeId = $queryNode;
        if (!$iterator) {
            $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        }
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $nodeId) { //Advance iterator until we reach the designated node
            $iterator -> next();
        }
        if ($iterator -> valid()) { //If we found the designated node, advance the iterator once more to get its next node
            $iterator -> next();
        } else {
            throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$nodeId, EfrontTreeException :: NODE_NOT_EXISTS);
        }
        if ($iterator -> valid()) { //If there is a next node, get it
            $nextNode = $iterator -> current();
            if (!$queryNode) {
                $this -> currentNodeId = $nextNode['id']; //If a $queryNode was not specified, we must advance the internal pointer, so assign the current node pointer to the next node
            }
            return $nextNode;
        } else {
            return false;
        }
    }
    /**

     * Get the previous node in the tree

     *

     * The function returns the specified node's previous node

     * <br/>Example:

     * <code>

     * $content = new EfrontContentTree(4);				//Create the content tree for lesson with id 4

     * $node = $content -> getFirstNode();				//$node now holds the array of the content tree's first node

     * $node = $content -> getNextNode();				//$node now holds the array of the content tree's second node

     * $node = $content -> getPreviousNode(32);			//$node now holds the array of node's 32 previous node

     * $node = $content -> getPreviousNode();			//$node now holds the array of the content tree's first node

     * </code>

     * If the specified node is the last one, false is returned

     *

     * @param int $queryNode A node id to get its previous node

     * @param ArrayIterator $iterator An specific iterator to use, instead of the default one

     * @return array The previous node array

     * @since 3.5.0

     * @access public

     */
    public function getPreviousNode($queryNode, $iterator = false) {
        $queryNode instanceof ArrayObject ? $nodeId = $queryNode['id'] : $nodeId = $queryNode;
        if (!$iterator) {
            $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        }
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $nodeId) { //Advance iterator until we reach the designated node
            $previousNode = $iterator -> current();
            $iterator -> next();
        }
        if ($iterator -> valid()) { //If we found the designated node, $previousNode now holds the previous node
            if (!isset($previousNode)) { //The designated node was apparently the first one, so return false
                return false;
            } else {
             if (!$queryNode) {
                 $this -> currentNodeId = $previousNode['id']; //If a $queryNode was not specified, we must set the internal pointer to the previous node we just found
             }
             //$previousNode = $this -> filterOutChildren(new RecursiveArrayIterator($previousNode));     //Cut off node's children
             return $previousNode;
            }
        } else {
            throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$nodeId, EfrontTreeException :: NODE_NOT_EXISTS);
        }
    }
    /**

     * Seeks designated node

     *

     * This function seeks the node with the designatd id

     * in the content tree and returns its properties

     * <br/>Example:

     * <code>

     * $content = new EfrontContentTree(4);				//Create the content tree for lesson with id 4

     * $node = $content -> seekNode(43);				//Returns node with id 43

     * </code>

     *

     * @param int $queryNode The node id

     * @return array The node array

     * @since 3.5.0

     * @access public

     */
    public function seekNode($queryNode) {
        $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $queryNode) { //Advance iterator until we reach the designated node
            $iterator -> next();
        }
        if ($iterator -> valid()) {
            return $iterator -> current();
        } else {
            throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$queryNode, EfrontTreeException :: NODE_NOT_EXISTS);
        }
    }
    /**

     * Get node children

     *

     * This function returns the tree branch holdning the designated node

     * and its children

     * <br/>Example:

     * <code>

     * $content = new EfrontContentTree(4);		//Create the content tree for lesson with id 4

     * $content -> getChildren(54);				//Get node's 54 children

     * </code>

     * The function uses RecursiveArrayIterator :: getChildren() to get the branch

     *

     * @param mixed $node Either the node id or an EfrontNode object

     * @return RecursiveArrayIterator The tree branch

     * @since 3.5.0

     * @access public

     * @see RecursiveArrayIterator :: getChildren()

     */
    public function getNodeChildren($node) {
        $node instanceof ArrayObject ? $nodeId = $node['id'] : $nodeId = $node;
        $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $nodeId) { //Advance iterator until we reach the designated node
            $iterator -> next();
        }
        if ($iterator -> valid()) { //If we found the designated node
            return $iterator -> getChildren();
        } else {
            throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$nodeId, EfrontTreeException :: NODE_NOT_EXISTS);
        }
    }
    /**

     * Get node ancestors

     *

     * This function is used to get the node ancestors

     * <br/>Example:

     * <code>

     * </code>

     *

     * @param mixed $node Either the node id or an EfrontNode object

     * @return array An array of array obhects, ancestors of the

     */
    public function getNodeAncestors($node) {
        $node instanceof ArrayObject ? $nodeId = $node['id'] : $nodeId = $node;
        $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator($this -> tree, RecursiveIteratorIterator :: SELF_FIRST)); //Get an iterator for the current tree. This iterator returns only whole node arrays and not node members separately (such as id, timestamp etc)
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $nodeId) { //Forward iterator index until you reach the designated element, which has an index equal to the node id that will be removed
      $iterator -> next();
  }
  if ($iterator -> valid()) {
   $parents[] = $this -> filterOutChildren($iterator -> current());
   for ($i = $iterator -> getDepth(); $i > 0; $i--) { //Climb up the iterators and keep ancestor ids
       $parents[] = $this -> filterOutChildren($iterator -> getSubIterator($i)); //Get the corresponding nodes
   }
  } else {
      $parents = array();
  }
  return $parents;
    }
    /**

     * Filter out children

     *

     * This function removes the children from the designated tree node

     *

     * @param array $branch The tree branch

     * @return array The node without the children

     * @since 3.5.0

     * @access protected

     */
    protected function filterOutChildren($branch) {
        foreach (new removeNumericIndicesFilterIterator(new RecursiveArrayIterator($branch)) as $key => $value) { //Keep only associative array keys and drop numerical, which hold the children
            $node[$key] = $value;
        }
        return $node;
    }
    /**

     * Return a flat representation of the tree

     *

     * This function is used to return a serial flat representation of the tree.

     * The nodes are presented ased on the proper succession, using depth-first

     * search.

     * <br/>Example:

     * <code>

     * $content = new EfrontContentTree(4);					//Create the content tree for lesson with id 4

     * $array = $content -> getFlatTree();					//$array now is an array of nodes

     * </code>

     *

     * @return $array The flat tree representation

     * @since 3.5.0

     * @access public

     */
    public function getFlatTree() {
       $flatTree = array();
       foreach (new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)) as $key => $value) { //We create new iterators (in order to leave unchanged the internal tree pointer) and advance them to the end of the tree
           $flatTree[] = $this -> filterOutChildren($value);
       }
       return $flatTree;
 }
 /**

	 *

	 * @param unknown_type $node

	 * @return unknown_type

	 */
 public function isLeaf($node) {
     $node instanceof ArrayObject ? $nodeId = $node['id'] : $nodeId = $node;
     $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
     $iterator -> rewind();
     //Initialize iterator
     while ($iterator -> valid() && $iterator -> key() != $nodeId) { //Advance iterator until we reach the designated node
         $iterator -> next();
     }
     if ($iterator -> valid()) {
         $iterator = new EfrontNodeFilterIterator(new IteratorIterator(new ArrayIterator($iterator -> current())));
         $iterator -> rewind(); //Initialize iterator
         if ($iterator -> valid()) { //Advance iterator until we reach the designated node
             return false;
         }
         return true;
     } else {
         throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$node['id'], EfrontTreeException :: NODE_NOT_EXISTS);
     }
 }
 public function isRoot($iterator = false, $node) {
  if($node['parent_content_ID'] == '0')
  {
   return true;
  }
  else
  {
   return false;
  }
       if (!$iterator) {
           $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create a new iterator, so that the internal iterator pointer is not reset
       }
       $iterator -> rewind(); //Initialize iterator
  if ($iterator -> current() -> offsetGet('id') == $node->offsetGet('id')) {
   return true;
  }
    else {
   return false;
    }
  }
 public function getLastChild($queryNode) {
        $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $queryNode['id']) { //Advance iterator until we reach the designated node
   $iterator -> next();
        }
   $iterator = new EfrontNodeFilterIterator(new IteratorIterator(new ArrayIterator($iterator -> current())));
         $iterator -> rewind(); //Initialize iterator
            $currentNode = $iterator -> current();
   while ($iterator -> valid()) { //Advance iterator until we reach the designated node
             $currentNode = $iterator -> current();
    $iterator -> next();
   }
   return $currentNode;
 }
  public function isLastChild($queryNode) {
     $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $queryNode['parent_content_ID']) { //Advance iterator until we reach the designated node
   $iterator -> next();
        }
   $iterator = new EfrontNodeFilterIterator(new IteratorIterator(new ArrayIterator($iterator -> current())));
         $iterator -> rewind(); //Initialize iterator
            $currentNode = $iterator -> current();
   while ($iterator -> valid()) { //Advance iterator until we reach the designated node
    $currentNode = $iterator -> current();
//	echo "-->".	$currentNode['id'];
    $iterator -> next();
   }
  //	echo $queryNode['content_ID']." == ". $currentNode['content_ID'];
   if($queryNode['content_ID'] == $currentNode['content_ID'] )
   {
  //		echo "TRUE";
    return true;
   }
   else
   {
  //		echo "FALSE";
    return false;
   }
   return $currentNode;
        $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $queryNode['parent_content_ID']) { //Advance iterator until we reach the designated node
   $iterator -> next();
        }
  if ($iterator -> valid()) {
   $iterator = new EfrontNodeFilterIterator(new IteratorIterator(new ArrayIterator($iterator -> current())));
         $iterator -> rewind(); //Initialize iterator
   while ($iterator -> valid() && $iterator -> key() != $queryNode['id']) { //Advance iterator until we reach the designated node
    $iterator -> next();
   }
   $iterator->next();
   if($iterator->valid())
   {
    return false;
   }
   else
   {
    return true;
   }
        } else {
            throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$queryNode['id'], EfrontTreeException :: NODE_NOT_EXISTS);
        }
 }
  public function getNextSiblingNode($queryNode) {
   $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $queryNode['parent_content_ID']) { //Advance iterator until we reach the designated node
   $iterator -> next();
        }
  if ($iterator -> valid()) {
   $iterator = new EfrontNodeFilterIterator(new IteratorIterator(new ArrayIterator($iterator -> current())));
         $iterator -> rewind(); //Initialize iterator
   while ($iterator -> valid() && $iterator -> key() != $queryNode['id']) { //Advance iterator until we reach the designated node
    $iterator -> next();
   }
   $iterator->next();
   if ($iterator -> valid())
   {
    return $iterator -> current();
   }
   else
   {
    throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$queryNode['id'], EfrontTreeException :: NODE_NOT_EXISTS);
   }
        } else {
            throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$queryNode['id'], EfrontTreeException :: NODE_NOT_EXISTS);
        }
   }
  public function getPreviousSiblingNode($queryNode) {
        $iterator = new EfrontNodeFilterIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($this -> tree), RecursiveIteratorIterator :: SELF_FIRST)); //Create iterators for the tree
        $iterator -> rewind(); //Initialize iterator
        while ($iterator -> valid() && $iterator -> key() != $queryNode['parent_content_ID']) { //Advance iterator until we reach the designated node
   $iterator -> next();
        }
  if ($iterator -> valid()) {
   $iterator = new EfrontNodeFilterIterator(new IteratorIterator(new ArrayIterator($iterator -> current())));
         $iterator -> rewind(); //Initialize iterator
   while ($iterator -> valid() && $iterator -> key() != $queryNode['id']) { //Advance iterator until we reach the designated node
    $previousNode = $iterator -> current();
    $iterator -> next();
   }
   if ($iterator -> valid())
   {
    if (!isset($previousNode)) { //The designated node was apparently the first one, so return false
     return false;
     } else {
             if (!$queryNode) {
                 $this -> currentNodeId = $previousNode['id']; //If a $queryNode was not specified, we must set the internal pointer to the previous node we just found
             }
             //$previousNode = $this -> filterOutChildren(new RecursiveArrayIterator($previousNode));     //Cut off node's children
             return $previousNode;
   }
   }
        } else {
            throw new EfrontTreeException(_NODEDOESNOTEXIST.': '.$queryNode['id'], EfrontTreeException :: NODE_NOT_EXISTS);
        }
  }
    /**

     * Insert node to the tree

     *

     * @param mixed $node

     * @param mixed $parentNode

     * @param mixed $previousNode

     * @since 3.5.0

     * @access public

     * @abstract

     */
 abstract public function insertNode($node, $parentNode = false, $previousNode = false);
 /**

	 * Remove node from tree

	 *

	 * @param mixed $node

     * @since 3.5.0

     * @access public

     * @abstract

	 */
 abstract public function removeNode($node);
 /**

	 * Reset/initialize tree

	 *

     * @since 3.5.0

     * @access public

     * @abstract

	 */
 abstract function reset();
}
/**

 * Filter out subarrays and numerical indices

 *

 * @package eFront

 */
class EfrontAttributesOnlyFilterIterator extends FilterIterator
{
    /**

     * Filter out subarrays and numerical indices

     *

     * @return boolean

     */
    function accept() {
        return !is_numeric($this -> key()) && !is_array($this -> current());
    }
}
class removeNumericIndicesFilterIterator extends FilterIterator
{
    /**

     * Filter out numerical indices

     *

     * @return boolean

     */
    function accept() {
        return !is_numeric($this -> key());
    }
}
/**

 * Filter out entries with(out) the specified fields

 * WARNING: Cannot be used in conjuction with EfrontNodeFilterIterator!

 *

 * @package eFront

 */
class EfrontAttributeFilterIterator extends FilterIterator
{
 /**

	 * Filter mode

	 *

	 * @var mixed filter mode

	 */
    protected $mode;
    /**

     * Constructor

     *

     * @param unknown_type $it

     * @param unknown_type $mode

     */
    function __construct($it, $mode = false) {
        parent::__construct($it);
        is_array($mode) ? $this -> mode = $mode : $this -> mode = array($mode);
    }
    /**

     * Keep only specified attributes

     *

     * @return unknown

     */
    function accept() {
        return in_array($this -> key(), $this -> mode);
    }
}
/**

 * Keep only nodes (and not leaves)

 *

 * @package eFront

 */
class EfrontNodeFilterIterator extends FilterIterator
{
    protected $mode;
    protected $evaluate;
    /**

     * $evaluate sets if the mode will be evaluated to true or false

     */
    function __construct($it, $mode = false, $evaluate = true) {
     parent::__construct($it);
     $this -> mode = $mode;
        $this -> evaluate = $evaluate;
    }
    function accept() {
     if ($this -> mode) {
            $accepted = true;
            $current = $this -> current();
            foreach ($this -> mode as $key => $value) {
                if (!isset($current[$key]) || $current[$key] != $value) {
                    $this -> evaluate ? $accepted = false : $accepted = true;
                }
            }
            return $this -> current() instanceof ArrayObject & $accepted;
        } else {
            return $this -> current() instanceof ArrayObject;
        }
    }
}
