<?php
    ERROR_REPORTING(-1);
    
class Node {

    private $parentNode = NULL;
    private $child = array();
    protected $name;
    

    public function __construct($name) {
        $this->name = $name;
    }
    public function __toString() {
        $node = "\nNameNode: " . $this->name . "\n";
        $node .= "ParentName: " . $this->parentNode->getName() . "\n";
        foreach ($this->child as $child) {
            $node .= "ChildName: " . $child->getName() ."\n";
        }
        return $node;
    }
    public function getName() {
        return $this->name;
    }
    
    public function getParentNode(){ 
        if (isset($this->parentNode)) {
            return $this->parentNode;
        }
        return NULL;
    }
    public function getChildrenNode() {
        return $this->child;
    }
    
    public function getAllParents(){
        if (NULL == $this->getParentNode()) {
            return array();
        } else {
            $parent = $this->getParentNode();
            $parents = $parent->getAllParents();
            $parents[] = $parent;
            return $parents;
        }
    }
    public function getAllChildren(){
        $childrenNode = $this->getChildrenNode();
        $children = $childrenNode;
        foreach ($childrenNode as $child) {
            $children = array_merge($children, $child->getAllChildren());
        }
        
        return $children;
    }
    
    public function getDepth() {
        $children = $this->child;
        $depth = -1;
        //echo $this->name() . '->';
        foreach ($children as $child) {
            $depth = max($depth, $seed->getDepth());
        }
        $depth++;
        return $depth;
    }
    
    public function getDescendantsCount(){
        $DescendantsCount = 0;
        $children = $this->child;
        echo $this->getName() . '->';
        foreach ($children as $child) {
            $DescendantsCount++;
            $DescendantsCount += $child->getDescendantsCount();
        }
        return $DescendantsCount;
    }
    
    public function appendChild(Node $child) {
        if ($this == $child) {
            try {
                throw new Exception("ОШИБКА! Нельзя делать обьект " . $this->getName() . " родителем самого себя.\n");
            } catch(Exception $e) {
                echo $e->getMessage();
            }
        }
        $child->remove();
        
        $this->child[] = $child;
        $child->parentNode = $this;
    }

    public function remove(){
        if (isset($this->parentNode)) {
            echo "\nУдаление\n";
            $parentNode = $this->parentNode;
            
            $children = $parentNode->child;
            $key = array_search($this , $children);
            unset($parentNode->child[$key]);
            
            $this->parentNode = null;
        }
    }
    
    public function getNextSibling(){
        $parentNode = $this->parentNode;
        if (NULL ==$parentNode) {
            return NULL;
        }
        $siblings = $parentNode->getChildrenNode();

        $key = array_search( $siblings, $this);
        if (isset($siblings[$key+1])) {
            return $siblings[$key+1];
        } else { 
            return NULL;
        }
    }
    public function getPreviousSibling(){
        $parentNode = $this->parentNode;
        if (NULL ==$parentNode) {
            return NULL;
        }
        $siblings = $parentNode->getChildrenNode();
        $key = array_search( $siblings, $this);
        if (isset($siblings[$key-1])) {
            return $siblings[$key-1];
        } else { 
            return NULL;
        }
    }
    
    
    public function walk($function) {
        $allChildren = $this->getAllChildren();
        $return = array();
        $callback = 
            function ( $child ) use ( $function, &$return) {
                $return[] = "Имя узла: " . $child->getName() . "    Количество потомков: "
                . $child->$function();
            };
        array_walk( $allChildren, $callback );
        print_r($return);
    }
    // обходит всех потомков $node, для каждого вызывая функцию
        

    
}
    
    
// Проверим работу nextSibling
$node1 = new Node('node1');

$node10 = new Node('node10');
$node11 = new Node('node11');
$node12 = new Node('node12');
$node13 = new Node('node13');

$node1->appendChild($node10);
$node1->appendChild($node11);
$node1->appendChild($node12);
$node1->appendChild($node13);

// провеим кто идет за node10
$next = $node10->getNextSibling();
echo $next;

$node12->remove();
$node11->remove();

// проверим — должно получиться node13
$next2 = $node10->getNextSibling();
var_dump($next2);


