<?php
class TreeElement
{
  protected $nodeName;
  protected $childNodes=[];
  protected $parentNode;
  
  
  public function __construct($nodeName){
    $this->nodeName = $nodeName;
  }
  
  public function getNodeName(){
    return $this->nodeName;
  }
  
  public function findDescendant($name){
    foreach($this->childNodes as $child){
      if($child->getNodeName()==$name){
        return $child;
      }
      else{
        $desc = $child->findDescendant($name);
        if($desc && $desc->getNodeName()==$name){
          return $desc;
        }
      }
    }
    return null;
  }

  public function countChildNodes(){
    return count($this->childNodes);
  }

  public function countDescendants(){
    $descCount = 0;
    foreach ($this->childNodes as $child) {
     $descCount++;
     $descCount+= $child->countDescendants();
    }
    return $descCount;
  }
  
  
  public function addChild(TreeElement $child){
    if($this->getRootElement()->findDescendant($child->getNodeName())){
      return false;
      
    }
    $this->childNodes[] = $child;
    $child->setParentNode($this);
  }

  public function removeChild($node){
    foreach ($this->childNodes as $key => $child) {
      if($child === $node){
        unset($this->childNodes[$key]);
      }
    }
  }

  public function isRoot(){
    if(!$this->parentNode){
      return true;
    }
    return false;
  }

  public function getRootElement(){
    $currentNode = $this;
    while (!$currentNode->isRoot()){
      $currentNode = $currentNode->parentNode;
    };
    return $currentNode;
  }

  public function getDepth(){
    $depth = 0;
    if($this->parentNode){
      $depth++;
      $depth+= $this->parentNode->getDepth();
    }
    return $depth;
  }

  public function getChildNodes(){
    return $this->childNodes;
  }

  public function setChildNodes($newNodes){
    $this->childNodes = [];
    foreach ($newNodes as $child) {
     $this->addChild($child);
    }
  }

  public function getParentNode(){
    return $this->parentNode;
  }

  public function setParentNode($node){
    $this->parentNode = $node;
  }

  public function isDescendant($node){
    if($node->isAncestor($this)){
      return true;
    }
    return false;
  }

  public function isAncestor($node){
    foreach ($this->childNodes as $child) {
      if($node===$child){
        return true;
      }
      elseif($child->isAncestor($node)){
        return true;
      }
    }
    return false;
  }

  public function getNextSibling(){
    $parent = $this->parentNode;
    if(!$parent){
      return false;
    }
    $previous = NULL;
    foreach ($parent->getChildNodes() as $child) {
      if($previous==$this){
        return $child;
      }
      $previous = $child;
    }
    return false;
  }

  public function getPreviousSibling(){
    $parent = $this->parentNode;
    $previous = NULL;
    if(!$parent){
      return false;
    }
    foreach ($parent->getChildNodes() as $child) {
      if($this == $child){
        return $previous;
      }
      $previous = $child;
    }
    return false;
  }

  public function moveSibling($n){
    $parent = $this->parentNode;
    if(!$parent){
      return false;
    }
    $children = $parent->getChildNodes();
    foreach ($children as $key => $child) {
      if($child===$this){
        $element = array_splice($children, $key, 1);
        array_splice($children, $n, 0, $element);
        break;
      }
    }
    $parent->setChildNodes($children);
  }

  public function moveNode($parent){
    if(!$this->parentNode){
      return false;
    }
    $this->parentNode->removeChild($this);
    $parent->addChild($this);
  }

  public function displayAsCatalog(){
    $children = "";
    $depth = "";
    if($this->countDescendants()>0){
      $children = " (".$this->countDescendants().") ";
    }

    for($i = 0; $i<$this->getDepth();$i++){
      $depth.="...";
    }
    echo $depth.$this->nodeName.$children."\n";
    foreach ($this->childNodes as $child) {
      $child->displayAsCatalog();
    }
  }
}


$tech = new TreeElement("Бытовая техника");
$tech->addChild(new TreeElement('Телевизоры'));
$televisors = $tech->findDescendant('Телевизоры');
$televisors->addChild(new TreeElement('LCD-телевизоры'));
$televisors->addChild(new TreeElement('Плазменные'));
$tech->addChild(new TreeElement('Холодильники'));
$refrigirators = $tech->findDescendant('Холодильники');
$refrigirators->addChild(new TreeElement('Маленькие'));
$refrigirators->addChild(new TreeElement('Средние'));
$refrigirators->addChild(new TreeElement('Большие'));
$tech->displayAsCatalog();