<?php
function prepareTextToTree($str)
{
    return str_replace('"', "&quot;", str_replace('<', "&lt;", str_replace('>', "&gt;", str_replace('&', "&amp;", str_replace('\\', "\\\\", $str))))); // 69553, 71169
}

class Tree
{
    protected $_items = array();
    protected $_childs = array();

    /**
     * Проверка на наличие элемента
     * @param mixed $id
     * @return boolean
     */
    public function itemExists($id){
        return isset($this->_items[$id]);
    }
    /**
     * Получить кол-во элементов дерева.
     * @return integer
     */
    public function getCount(){
        return sizeof($this->_items);
    }
    /**
     * Добавление элемента в дерево
     * @param mixed $id
     * @param mixed $parent
     * @param mixed $data
     * @return void
     */
    public function addItem($id , $parent = 0 , $data)
    {
       $this->_items[$id] = array(
            'id'=>$id ,
            'parent'=>$parent ,
            'data'=>$data
        );

        if(!isset($this->_childs[$parent])){
            $this->_childs[$parent] = array();
        }

       /*
        * Cсылка использована преднамеренно, на производительность не влияет,
        * но позволяет изменять элементы, например, при внедрении сортировки
        */
        $this->_childs[$parent][$id] = & $this->_items[$id];
    }
    /**
     * Получить элемент по индификатору
     * @param mixed $id
     * @throws Exception
     * @return array
     */
    public function getItem($id){
        if($this->itemExists($id))
            return $this->_items[$id];
        else
            throw new Exception('wrong id');
    }
    /**
     * Проверка на наличие дочерних элементов
     * @param mixed $id
     * @return boolean
     */
    public function hasChilds($id) {
        return isset($this->_childs[$id]);
    }
    /**
     * Получить дочерние элементы
     * @param mixed $id
     * @return array
     */
    public function getChilds($id){
         if(!$this->hasChilds($id))
            return array();
          return $this->_childs[$id];
    }
    /**
     * Рекурсивное удаление элементов (узел + дочерние)
     * @param mixed $id
     * @return void
     */
    protected function _remove($id){
       /*
        * Получаем дочерние элементы
        */
       $childs = $this->getChilds($id);
       if(!empty($childs)){
          /*
           * Рекурсивное удаление дочерних элементов
           */
           foreach ($childs as $k=>$v){
               $this->_remove($v['id']);
           }
       }
       /*
        * Удаляем узел элемента
        */
       if(isset($this->_childs[$id]))
           unset($this->_childs[$id]);
       /*
        * Получаем id родительского узла
        */
       $parent = $this->_items[$id]['parent'];
       /*
        * Удаляем из родительского узла ссылку на дочерний
        */
        if(!empty($this->_childs[$parent])){
            foreach ($this->_childs[$parent] as $key=>$item){
                if($item['id']==$id){
                        unset($this->_childs[$parent][$key]);
                        break;
                }
            }
         }
       /*
        *  Удаляем элемент
        */
       unset($this->_items[$id]);
    }
    /**
     * Удаление узла
     * @param mixed $id
     * @return void
     */
    public  function removeItem($id){
        if($this->itemExists($id))
           $this->_remove($id);
    }
    /**
     * Перемещение узла
     * @param mixed $id
     * @param mixed $newParent
     * @return void
     */
    public function changeParent($id , $newParent)
    {
        if($this->itemExists($id) && ($this->itemExists($newParent) || $newParent === 0))
        {
             $oldParent = $this->_items[$id]['parent'];
             $this->_items[$id]['parent'] = $newParent;
             if(!empty($this->_childs[$oldParent]))
             {
                foreach ($this->_childs[$oldParent] as $k=>$v)
                {
                        if($v['id']===$id)
                        {
                           unset($this->_childs[$oldParent][$k]);
                           break;
                        }
                }
             }
             $this->_childs[$newParent][$id] = & $this->_items[$id];
        }
    }
    //======================================
    /**
     * метод отрисовки  узла
     * @param mixed $parent
     * @return string
     */
    protected function _createGroup($parent){
      $s='[';
      $first = true;
      $childs = $this->getChilds($parent);
      $total = count($childs);
      $counter = 0;
      foreach ($childs as $k=>$v){
        $counter++;
        if (!$first) { $s.= ','; }
        $hchld = ($v['data']['o_iright'] - $v['data']['o_ileft']) != 1 ;
        $s.='{ ';
        $s.= ' "text": "'.prepareTextToTree(nl2br($v['data']['o_name'])).'",';
        $s.= ' "name":"'.str_replace('"', "&quot;",$v['data']['o_guid']).'", ';
        $s.= ' "id":"'.str_replace('"', "&quot;", $v['data']['idobject']).'", ';
        $s.= ' "src_guid":"'.str_replace('"', "&quot;", $v['data']['src_guid']).'", ';
        $s.= ' "icon":  "'.getImgIcon($v['data']['o_icon']).'"';
        if ($hchld) {
          $s.= ', "hasChildren": true ';
        }
        if($counter == $total){
          $s.= ', "lastElement": true ';
        }
        if($this->hasChilds($v['id'])){
          $s.=', "expanded": true, "children": ';
          $s.=$this->_createGroup($v['id']);
        }
        $first = false;
        $s.=' }';
      }
      $s.=']';
      return $s;
    }

    //======================================
    /**
     * метод отрисовки  узла
     * @param mixed $parent
     * @return string
     */
    protected function _createHTMLGroup($parent){
        $s = '';
        $childs = $this->getChilds($parent);
        foreach ($childs as $k=>$v){
          $hchld = false;
          $cl_op = ($this->hasChilds($v['id'])) ? ' class="open"' : '';
          if ($cl_op == '' && ( ($v['data']['o_iright'] - $v['data']['o_ileft']) != 1 ) ) {
            $cl_op = ' class="hasChildren"';
            $hchld = true;
          }
          $s .= '<li id="'.$v['data']['idobject'].'" name="'.$v['data']['o_guid'].'" '.$cl_op.'><nobr><span><img src="'.$urlWO.''.getImgIcon($v['data']["o_icon"]).'" class="navigation_image"/>';
          if ((!empty($v['data']['src_guid'])) && (strlen($v['data']['src_guid']) > 0)){
            $s .= '<a href="javascript:clickTreeMessage(\''.$v['data']['src_guid'].'\', \'*\')" o_guid="'.$v['data']['o_guid'].'"';
          } else {
            $s .= '<a href="javascript:clickTree(\''.$v['data']['o_guid'].'\')" o_guid="'.$v['data']['o_guid'].'"';
          }

          $s .= 'title="'.htmlspecialchars(prepareTextToTree(nl2br($v['data']['o_name']))).'">'.prepareTextToTree(nl2br($v['data']['o_name'])).'</a></span></nobr>'; // deleted htmlspecialchars Task 62821
          

        if ($hchld) {
          $s .=' <ul>';
          $s .='	<li><span class="placeholder">&nbsp;</span></li>';
          $s .=' </ul>';
        }

        if($this->hasChilds($v['id'])){
              $s .= '<ul>';
                $s .= $this->_createHTMLGroup($v['id']);
        $s .= ' </ul>';
          }
            $s .='</li>';
        }
        return $s;
    }

    protected function _createHTMLSearchGroup($parent){
    	$s = '';
        $childs = $this->getChilds($parent);
        foreach ($childs as $k=>$v){
 			$name = 'name="userepfilter[]"';
 			$chkid = 'checkbox_reptree_node';
 			$classes = 'class="'.$v['data']['parent_idobject'].'"';
        	$hchld = false;
        	 
 			if ( ( ($v['data']['o_iright'] - $v['data']['o_ileft']) != 1 )  ) {
 				//$name = ''; Task 75685
        		$classes = 'class="'.$v['data']['idobject'].'"';
        		$chkid = 'checkbox_reptree_root';
        		$hchld = true;
 			}

			$s .= '<li id="'.$v['data']['idobject'].'_'.$v['data']['idreport'].'" name="'.$v['data']['r_name'].'" ><nobr><span><img src="'.$urlWO.''.getImgIcon($v['data']["o_icon"]).'" class="navigation_image"/> <input type="checkbox" '.$name.' '.$classes.' id="'.$chkid.'" class="objectcheckbox" value="'.$v['data']['idobject'].'_'.$v['data']['idreport'].'" />'.prepareTextToTree($v['data']['r_name']).'</span></nobr>';

   			if($this->hasChilds($v['id']) ){
            	$s .= '<ul>';
                $s .= $this->_createHTMLsearchGroup($v['id']);
 				$s .= ' </ul>';
      		}
            $s .='</li>';
        }
        return $s;
    }

    public function createServiceHtml($startItem){
        return $this->_createGroup($startItem);
    }

    public function createHtml($startItem){
        return $this->_createHTMLGroup($startItem);
    }

    public function createHtmlSearch($startItem){
        return $this->_createHTMLsearchGroup($startItem);
    }
}
?>