<?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, для каждого вызывая функцию
        

    
}
    
    
//создаём узлы 1 -> 2
//               -> 3 -> 4
//               -> 5
$node1 = new Node('node1');

$node2 = new Node('node2');
$node1->appendChild($node2);

$node3 = new Node('node3');
$node2->appendChild($node3);

$node4 = new Node('node4');
$node3->appendChild($node4);

$node5 = new Node('node5');
$node1->appendChild($node5);

$node6 = new Node('node6');
$node4->appendChild($node6);

/*
echo "До удаления\n";
var_dump(array_map(function ($x){ return $x->getName(); }, $node1->getChildrenNode()));

$node3->remove();
echo "После \n";
var_dump(array_map(function ($x){ return $x->getName(); }, $node1->getChildrenNode()));
*/


$function = 'getDescendantsCount';
$node1->walk($function);
