<?php
/**
* @author:  Bunzia Alexander <nifus@mail.ru> <http://www.weblancer.net/users/nifus/>
* @copyright: Copyright (c) 2010, Bunzia Alexander
* @version: 2.0
* @license: http://www.gnu.org/copyleft/gpl.html GNU/GPL
*/

include_once(MAIN_SOURCE_PATH.'/inc/class.query.php');
include_once(MAIN_SOURCE_PATH.'/inc/class.objects.php');

class filedsGetnsTree
{

  
 	 static function lang_list($v){
    if ( false===MAIN_MULTILANG){
			return false;
		}
    
    $langs = explode(',',MAIN_LANGS);
    $s = new select_box();
    $s -> selected_id($v);
    $s -> empty_record(false);
    foreach( $langs as $lng )
    {
      $s -> set($lng,$lng);
    }
    return $s -> get();
  }
}

class nsTreeQuery extends query{


  public $fields_get = 'filedsGetnsTree';


	public function __construct($table_name,$alias='c',$fields=array('id'=>'c_id','left'=>'c_left','right'=>'c_right','level'=>'c_level')) {
	global $MAIN_DB;
			$this -> set_table( $MAIN_DB -> prefix($table_name) );
			$this -> without_prefix_table  = $table_name;
			$this -> set_alias($alias);
			if ( empty($fields) ){
				throw new Exception('Ошибка:нужно передать названия стобцов!');
			}
			foreach( $fields as $k=>$v ){
				$this -> $k = $v;
			}
			
	}

	/**
	*	выбираем запись по уникальному ключу id	
	*
	*/
	public function where_id($v){
		$v = intval($v);
		if ( empty($v)  ){
			return FALSE;
		}
		return $this -> set_where( $this -> alias.'.'.$this -> id,$v);
	}
		
	/**
	*	выбираем записи уровня v
	*
	*/
	public	function where_level($v){
		$v = intval($v);
		if ( empty($v) ){
			return false;
		}
		return $this -> set_where( $this -> alias.'.'.$this -> level,$v);
	}
	
	/**
	*	выбираем записи в которых столбец k больше значения v
	*
	*/
	public	function where_more($k,$v){
		if ( empty($k) ){
			return false;
		}
		return $this -> set_anothe_where( $this -> alias.'.'.$k.'>'.$v);
	}


	public	function where_down($v){
		$v = intval($v);
		if ( empty($v) ){
			return FALSE;
		}
		list($l,$r,$level) = nsTree::getServiceInfo( $this -> without_prefix_table ,$v);
		$this -> set_anothe_where( $this -> alias.'.'.$this -> left.'>='.$l);
		$this -> set_anothe_where( $this -> alias.'.'.$this -> right.'<='.$r);
		$this -> set_anothe_where( $this -> alias.'.'.$this -> level.'>'.$level);
		return true;
	}
	

  /**
  *	вверх по дереву. получим всё что ниже уровня категории v
  *
  */
	public	function where_up($v){
		$v = intval($v);
		if ( empty($v) ){
			return FALSE;
		}
		list($l,$r,$level) = nsTree::getServiceInfo( $this -> without_prefix_table ,$v);
		$this -> set_anothe_where( $this -> alias.'.'.$this -> left.'<='.$l);
		$this -> set_anothe_where( $this -> alias.'.'.$this -> right.'>='.$r);
		$this -> set_anothe_where( $this -> alias.'.'.$this -> level.'!=0');
		return true;
	}
	
	
	/**
	*	выбираем записи в которых столбец k меньше значения v
	*
	*/
	public	function where_less($k,$v){
		if ( empty($k) ){
			return false;
		}
		return $this -> set_anothe_where( $this -> alias.'.'.$k.'<'.$v);
	}

	public function where_link($v){
		if ( false===MAIN_MULTILANG){
			return false;
		}
		$v = intval($v);
		if ( empty($v) ){
			return false;
		}
		return $this -> set_where( $this -> alias.'.link_id',$v);
	}
	
	 public function where_lang($v){
    if ( false===MAIN_MULTILANG){
			return false;
		}
    $langs = explode(',',MAIN_LANGS);
		if ( !in_array($v,$langs) ){
		  return false;
		}
		return $this -> set_where( $this -> alias.'.lang',$v);
	}

	public function where_main_rows(){
		if ( false===MAIN_MULTILANG){
			return false;
		}
		return $this -> set_anothe_where( $this -> alias.'.'.$this -> id.'='.$this -> alias.'.link_id');
	}
		
	public function order_left($v){
		return $this ->set_orderby($this -> alias.'.'.$this -> left,$v);
	}

	public function orderby_id($v){
		return 	$this -> set_orderby($this -> alias.'.'.$this -> id,$v);
	}


}


class nsTree extends objects{
	public $key='c_id';
	static $id='c_id';
	static $left = 'c_left';
	static $right = 'c_right';
	static $level = 'c_level';

	var $parent_id = false;

	static function query($table,$alias){

		return new nsTreeQuery( $table,$alias,array('id'=>self::$id,'left'=>self::$left,'right'=>self::$right,'level'=>self::$level) );
	}

	static function create($table){
		$o = new self( array(0,'c_id'=>0) );
		$o -> table = $table;
		return $o; 
	}

	static function searchById($table,$id){
		return load_id($table,$id);
	}

	static function load_id($table,$id){
	
		$m = self::query( $table,'m');
		if ( false===$m -> where_id($id) ){
			return false;
		}
		$m -> get('*');
		if (  $m -> get_count_rows()==0 ){
			return FALSE;
		}
		$o =  new self($id,$m -> row());
		$o -> table = $table;
		return $o;
	}
		
	/**
	*	получаем следующий элемент в текущей ветке
	*
	*/
	static function getNext($table,$id){
	  die('функция не реализованна');
		/*$node_info = nsTree::get_service_id($id);
		if ( false===$node_info ){
			return false;
		}
		list($c_left,$c_right,$c_level) = $node_info;
		
		
		return $row;*/
	}
	
		
	/**
	*	получаем предыдущий элемент в текущей ветке
	*
	*/
	static function getPrev($table,$id){
		$n =  self::query( $table,'a');
		if ( false===$n -> where_id($id) ){
			return false;
		}
		$n -> get(self::$left.','.self::$right.','.self::$level,false);
		$row = $n -> row();
		if ( false===$row ){
			return false;
		}
		return $row;
	}

	static function getParent($table,$id){
	  if ( empty($id) ){
		  return false;
		}
		list($l,$r,$level) = self::getServiceInfo($table,$id);
		if ( empty($l) ){
		  return false;
		}
		$level--;
		$p = self::query($table,'a');
		$p -> where_less(self::$left,$l);
		$p -> where_more(self::$right,$r);
		$p -> where_level($level);
		$p -> get(self::$id);
		list($c_id) = $p -> row();
		if ( false==$c_id ){
			return false;
		}
		return $c_id;
	}

	
	

	/**
	*	найдём все дочерние элементы
	*
	*/
	static function getChilds($table,$id){
	  if ( empty($id) ){
		  return false;
		}
		list($l,$r,$level) = self::getServiceInfo($table,$id);
		if ( empty($l) ){
		  return false;
		}
		$p =  self::query($table,'a');
		$p -> where($level);
		$p -> get( self::$id );
		list($c_id) = $p -> row();
		if ( false==$c_id ){
			return false;
		}
		return $c_id;
	}


	static function getServiceInfo($table,$id){

		$n =  self::query( $table,'q');
		//$n  -> set_debug(1);
		if ( false===$n -> where_id($id) ){
			return false;
		}
		$n -> get(self::$left.','.self::$right.','.self::$level,false);
		$row = $n -> row();
		if ( false===$row ){
			return false;
		}
		return $row;
	}


	

	public function id(){
		return $this -> info[self::$id];
	}	

	public function insert($parent_id){
	global $MAIN_DB;
   
		if ( empty($parent_id) ){
			throw new Exception(E_NSTREE_BAD_PARENT);
		}
		
			// получаем данные касающиеся ID
		list($left_id, $right_id, $level) = self::getServiceInfo($this -> table,$parent_id);
	
		if ( empty($left_id) ){
			throw new Exception(E_NSTREE_BAD_PARENT);
		}
	
			// creating a place for the record being inserted
		$sql = '
			UPDATE '.$MAIN_DB -> prefix($this -> table).' 
			SET 
				'.self::$left.	'=IF('.self::$left 	.'>'.	$right_id.','.self::$left. 	'+2,'.self::$left. '),
				'.self::$right.	'=IF('.self::$right .'>='.$right_id.','.self::$right. '+2,'.self::$right.')
			WHERE '.self::$right.'>='.$right_id;
		$MAIN_DB -> query($sql);
				
			// увеличиваем  правое смещение и уровень глубины
		$this -> set(self::$left,	$right_id);
		$this -> set(self::$right,($right_id+1) );
		$this -> set(self::$level,($level+1) );

		return parent::insert();
	}

	public function delete(){
	global $MAIN_DB;
		$id = $this -> id();

		list($left_id, $right_id, $level) = self::getServiceInfo($this -> table,$id);
			// корневой элемент удалить низяяя
		if ($level==0){
		  return FALSE;
		}


		if ( true===parent::delete() ){
			$sql = 
			'UPDATE '.$MAIN_DB -> prefix($this -> table).' 
			SET
				'.self::$left.'	=IF('.self::$left 	.' BETWEEN '.	$left_id 	.' AND '.	$right_id.','.self::$left.	'-1,'.self::$left.'),
				'.self::$right.'=IF('.self::$right 	.' BETWEEN '.	$left_id 	.' AND '.	$right_id.','.self::$right.	'-1,'.self::$right.'),
				'.self::$level.'=IF('.self::$left 	.' BETWEEN '.	$left_id 	.' AND '.	$right_id.','.self::$level.	'-1,'.self::$level.'),
				'.self::$left.'	=IF('.self::$left 	.' > '.				$right_id .','.										self::$left.	'-2,'.self::$left.'),
				'.self::$right.'=IF('.self::$right 	.' > '.				$right_id .','.										self::$right.	'-2,'.self::$right.') 
			WHERE c_right>'.$left_id;
			$MAIN_DB -> query($sql);
		}
		return true;
	}



	public function deleteAll() {
	global $MAIN_DB;
		
		$array_cat = array();
		$id = $this -> id();
		list($left_id, $right_id, $level) = self::getServiceInfo($this -> table,$id);

		if ( empty($left_id) || empty($right_id) ){
		  throw new Exception(E_NSTREE_BAD_PARENT);
		}

		$sql = 'SELECT '.self::$id.' FROM '.$MAIN_DB -> prefix($this -> table).' WHERE '.self::$left.' BETWEEN '.$left_id.' AND '.$right_id;
		$res = $MAIN_DB -> query($sql);
		while( list($c_id) = $MAIN_DB -> fetch_array($res) ){
			$array_cat[]=$c_id;
		}
		
		$sql = 'DELETE FROM '.$MAIN_DB -> prefix($this -> table).' WHERE '.self::$left.'  BETWEEN '.$left_id.' AND '.$right_id;
		$MAIN_DB -> query($sql);

		// Clearing blank spaces in a tree
		$delta_id = ($right_id - $left_id)+1;
		$sql= '
		  UPDATE '.$MAIN_DB -> prefix($this -> table).' 
		  SET 
		  	'.self::$left.	'=IF('.self::$left 	.'>'.$left_id.','.self::$left .	'-'.$delta_id.','.self::$left.'),
		  	'.self::$right.	'=IF('.self::$right .'>'.$left_id.','.self::$right. '-'.$delta_id.','.self::$right.')
		  WHERE '.self::$right.'>'.$right_id;
		$MAIN_DB -> query($sql);

		return $array_cat;
	}


	public function update($parent_id){

		parent::update();
			//	переносим ветку
		if ( !empty($parent_id) ){
				
			$this -> moveAll( $this -> id(), $parent_id);
		}
		return true;
	}

	public function moveAll($id, $new_parent_id) {
	global $MAIN_DB;
		
    if ($id==$new_parent_id) {
     	throw new Exception('Источником и приёмником является одна и та же ветка!');
		}

		list($leftId, $rightId, $level) = self::getServiceInfo($this -> table,$id);
		$parent = self::getParent($this -> table,$id);
		if ($parent==$new_parent_id) {
     	return false;
		}
	
		if ( empty($leftId) ){
			throw new Exception('Источник не существует!');
     }
       		//	получаем родительский 
    list($leftIdP, $rightIdP, $levelP) = self::getServiceInfo($this -> table,$new_parent_id);   
	  if ( empty($leftIdP) ){
			throw new Exception('Приёмник не существует!');
    }
	
      die('не работает');
    if ($leftIdP < $leftId && $rightIdP > $rightId && $levelP < $level - 1) {
    	$sql = '
    	UPDATE ' . $MAIN_DB -> prefix($this -> table). ' 
    	SET 
    		'.self::$level.' = CASE 
    			WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' 
    			THEN '.self::$level.' '.sprintf('%+d', -($level-1)+$levelP) . ' 
    			ELSE '.self::$level.'
    			END, 
    		'.self::$right.' = CASE 
    				WHEN '.self::$right.' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) . ' THEN '.self::$right.'-' . ($rightId-$leftId+1) . ' '. '
    				WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$right.'+' . ((($rightIdP-$rightId-$level+$levelP)/2)*2+$level-$levelP-1) . ' 
    				ELSE '.self::$right.' 
            END, 
        '.self::$left.' = CASE 
         		WHEN '.self::$left.' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) . ' THEN '.self::$left.'-' . ($rightId-$leftId+1) . ' ' . '
         		WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$left.'+' . ((($rightIdP-$rightId-$level+$levelP)/2)*2+$level-$levelP-1) . ' 
         		ELSE '.self::$left.' 
         		END '. '
      WHERE 
       '.self::$left.' BETWEEN ' . ($leftIdP+1) . ' AND ' . ($rightIdP-1);
    } elseif ($leftIdP < $leftId) {
    	$sql = '
    		UPDATE ' . $MAIN_DB -> prefix($this -> table) . ' 
    		SET 
    			'.self::$level.'  = CASE 
    				WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$level.' '.sprintf('%+d', -($level-1)+$levelP) . ' 
    				ELSE '.self::$level.' 
    				END, 
    			'.self::$left.' = CASE
    			 	WHEN '.self::$left.' BETWEEN ' . $rightIdP . ' AND ' . ($leftId-1) . ' THEN '.self::$left.'+' . ($rightId-$leftId+1) . ' '. '
    			 	WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$left.'-' . ($leftId-$rightIdP) . ' ELSE '.self::$left.'
    			 	END, 
    			'.self::$right.' = CASE 
    				WHEN '.self::$right.' BETWEEN ' . $rightIdP . ' AND ' . $leftId . ' THEN '.self::$right.'+' . ($rightId-$leftId+1) . ' '. '
    				WHEN '.self::$right.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$right.'-' . ($leftId-$rightIdP) . ' ELSE '.self::$right.' 
    				END '. '
    		WHERE 
    			('.self::$left.' BETWEEN ' . $leftIdP . ' AND ' . $rightId. ' '. 'OR '.self::$right.' BETWEEN ' . $leftIdP . ' AND ' . $rightId . ')';
    } else {
      $sql = '
      	UPDATE ' . $MAIN_DB -> prefix($this -> table) . ' 
      	SET 
      		'.self::$level.' = CASE 
      			WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$level.' '.sprintf('%+d', -($level-1)+$levelP) . ' ELSE '.self::$level.' 
      			END, 
      		'.self::$left.' = CASE 
      			WHEN '.self::$left.' BETWEEN ' . $rightId . ' AND ' . $rightIdP . ' THEN '.self::$left.'-' . ($rightId-$leftId+1) . ' '. '
      			WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$left.'+' . ($rightIdP-1-$rightId) . ' ELSE '.self::$left.'
      			END, 
      		'.self::$right.' = CASE 
      			WHEN '.self::$right.' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) . ' THEN c_right-' . ($rightId-$leftId+1) . ' '. '
      			WHEN '.self::$right.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$right.'+' . ($rightIdP-1-$rightId) . ' ELSE '.self::$right.' 
      			END '. '
      	WHERE 
      		('.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ' '. 'OR '.self::$right.' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ')';
        }
			$MAIN_DB -> query($sql);
      return TRUE;
    }

  static function order($table,$id,$new_id)
	{
	global $MAIN_DB;

		list($left_id, $right_id, $level) = self::getServiceInfo($table,$id);   
    if ( empty($left_id) ) {
      throw new Exception('Источник не существует!');
    }
		$count = $right_id - $left_id+1;	//	---	размер дерева
		
			//	---	элемент за которым будет размещён перемещаемый элемент
		list($left_id_p, $right_id_p, $level_p) = self::getServiceInfo($table,$new_id);  
		if ( empty($left_id_p) ) {
      throw new Exception('Приёмник не существует!');
    }

		if ( ($right_id_p+1)==$left_id )
		{
			throw new Exception('переносить некуда!');
		}
		
		if ( $left_id < $left_id_p ) 
		{
			$left_smech = ($right_id_p - $right_id);
			//	---	переносим вправо
			$sql = '
			UPDATE '.$MAIN_DB -> prefix($table).' 
			SET 
			  '.self::$left.'=
			  	IF('.self::$left.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$left.'+'.$left_smech.', 
			  		IF('.self::$left.' BETWEEN '.($right_id+1).' AND '.$right_id_p.','.self::$left.'-'.($count).','.self::$left.')),
				'.self::$right.'=
					IF('.self::$right.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$right.'+'.$left_smech.',
						IF('.self::$right.' BETWEEN '.($right_id+1).' AND '.$right_id_p.','.self::$right.'-'.($count).','.self::$right.')
				)';
		}
		else
		{
			$left_smech = ($left_id - $left_id_p)-1;
			//	---	переносим влево
			$sql = '
				UPDATE '.$MAIN_DB -> prefix($table).' 
				SET 
			    '.self::$left.'=
			    	IF('.self::$left.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$left.'-'.$left_smech.',
							IF('.self::$left.' BETWEEN '.($left_id_p+1).' AND '.($left_id-1).','.self::$left.'+'.($count).','.self::$left.')),
					'.self::$right.'=
						IF('.self::$right.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$right.'-'.$left_smech.',
							IF('.self::$right.' BETWEEN '.($left_id_p+1).' AND '.($left_id-1).','.self::$right.'+'.($count).','.self::$right.')
				)';
		}
		$MAIN_DB -> query($sql);
		return true;
	}
	


	
}