<?php

/**
 * <pre>
 * Invision Power Services
 * IP.Board v3.2.3
 * Very lightweight simple, basic node class
 * Owner: Matt Mecham
 * Last Updated: $Date: 2011-07-14 06:24:58 -0400 (Thu, 14 Jul 2011) $
 *
 * @example
 *
 * @author 		$Author: mmecham $
 * @copyright	(c) 2001 - 2009 Invision Power Services, Inc.
 * @license		http://www.invisionpower.com/community/board/license.html
 * @package		IP.Board
 * @link		http://www.invisionpower.com
 * @since		9th March 2005 11:03
 * @version		$Revision: 9234 $
 */

class classNodes
{
	/**
	 * Registry object
	 *
	 * @access	protected
	 * @var		object
	 */	
	protected $registry;
	
	/**
	 * Database object
	 *
	 * @access	protected
	 * @var		object
	 */	
	protected $DB;
	
	/**
	 * Settings object
	 *
	 * @access	protected
	 * @var		object
	 */	
	protected $settings;
	
	/**
	 * Request object
	 *
	 * @access	protected
	 * @var		object
	 */	
	protected $request;
	
	/**
	 * Language object
	 *
	 * @access	protected
	 * @var		object
	 */	
	protected $lang;
	
	/**
	 * Member object
	 *
	 * @access	protected
	 * @var		object
	 */	
	protected $member;
	protected $memberData;
	
	/**
	 * Cache object
	 *
	 * @access	protected
	 * @var		object
	 */	
	protected $cache;
	protected $caches;
	
	/**
	 * Item Cache
	 *
	 * @access	protected
	 * @var		array
	 */
	protected $_nodeCache = array();
	
	
	/**
	 * FIELD: item ID
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlPrimaryID  = '';
	
	/**
	 * FIELD: parent ID
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlParentID = '';
	
	/**
	 * FIELD: Is folder flag
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlFolderFlag = '';
	
	/**
	 * FIELD: Item name field
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlTitle     = '';
	
	/**
	 * SQL Field: From
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlTable        = '';
	
	/**
	 * SQL Fields: Additional 'where' information
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlWhere       = '';
	
	/**
	 * SQL Fields: Select information
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlSelect       = '*';
	
	/**
	 * SQL Fields: Node path
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlNodeLevel       = '';
	
	/**
	 * SQL Fields: Node left
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlNodeLeft       = '';
	
	/**
	 * SQL Fields: Node right
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlNodeRight       = '';
	
	/**
	 * SQL Fields: Order field
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlOrder       = '';
	
	/**
	 * SQL Fields: Pass IPB formatted add_join array
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlJoins       = '';
	
	/**
	 * SQL Fields: SQL limit
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlLimit       = null;
	
	/**
	 * SQL Fields: Tbl prefix used when using joins
	 *
	 * @access	protected
	 * @var		string
	 */
	protected $_sqlTblPrefix       = '';
	
	/**
	 * Debug cache
	 * Stores random debug messages and whatnot
	 *
	 * @access	protected
	 * @var		array
	 */
	protected $_debugMsgs = array();
	
	/**
	 * Method constructor
	 *
	 * @access	public
	 * @param	object		Registry Object
	 * @param	array 		Array of settings:
	 *						[ 'sqlPrimaryID'	   (the SQL table field for the item ID)
	 *						  'sqlParentID'  	   (the SQL table field for the parent ID)
	 *						  'sqlTitle'		   (the SQL table field for the item title)
	 *						  'sqlSelect'		   (the SQL table select fields (* by default))
	 *						  'sqlTable'		   (the SQL table name)
	 *						  'sqlNodeLevel'       (the SQL table field for the node level)
	 *						  'sqlNodeLeft'        (the SQL table field for node left)
	 *					      'sqlNodeRight'       (the SQL table field for node right)
	 *						  'sqlWhere'		   (Any additional 'where' information *Optional) ]
	 * @return	@e void
	 */
	public function __construct()
	{
		/* Make object */
		$this->registry   =  ipsRegistry::instance();
		$this->DB	      =  $this->registry->DB();
		$this->settings   =& $this->registry->fetchSettings();
		$this->request    =& $this->registry->fetchRequest();
		$this->member     =  $this->registry->member();
		$this->memberData =& $this->registry->member()->fetchMemberData();
		$this->cache	  =  $this->registry->cache();
		$this->caches     =& $this->registry->cache()->fetchCaches();
	}
	
	public function init( array $settings )
	{
		/* Sort out settings */
		$this->_sqlPrimaryID		 = isset( $settings['sqlPrimaryID']       ) ? $settings['sqlPrimaryID']       : '';
		$this->_sqlParentID		     = isset( $settings['sqlParentID']        ) ? $settings['sqlParentID']        : '';
		$this->_sqlNodeLevel	     = isset( $settings['sqlNodeLevel']        ) ? $settings['sqlNodeLevel']      : '';
		$this->_sqlNodeRight	     = isset( $settings['sqlNodeRight']       ) ? $settings['sqlNodeRight']       : '';
		$this->_sqlNodeLeft          = isset( $settings['sqlNodeLeft'] )        ? $settings['sqlNodeLeft']		  : '';
		$this->_sqlOrder	         = isset( $settings['sqlOrder']           ) ? $settings['sqlOrder']           : '';
		$this->_sqlTitle			 = isset( $settings['sqlTitle']           ) ? $settings['sqlTitle']           : '';
		$this->_sqlTable			 = isset( $settings['sqlTable']           ) ? $settings['sqlTable']           : '';
		$this->_sqlWhere			 = isset( $settings['sqlWhere']           ) ? $settings['sqlWhere']           : '';
		$this->_sqlSelect			 = isset( $settings['sqlSelect']          ) ? $settings['sqlSelect']          : $this->_sqlPrimaryID . ', ' . $this->_sqlParentID . ',' . $this->_sqlTitle . ',' . $this->_sqlNodeLeft . ',' . $this->_sqlOrder . ',' . $this->_sqlNodeLevel . ',' . $this->_sqlNodeRight;
		$this->_sqlJoins			 = isset( $settings['sqlJoins']			  ) ? $settings['sqlJoins']			  : array();
		$this->_sqlTblPrefix		 = isset( $settings['sqlTblPrefix']       ) ? $settings['sqlTblPrefix']       : 'xyxy';
		$this->_sqlLimit 			 = isset( $settings['sqlLimit']			  ) ? $settings['sqlLimit']			  : null;
	}
    	
    /**
     * Allow set/get data
     *
     */
    public function __call( $method, $arguments )
	{
		$firstBit = substr( $method, 0, 3 );
		$theRest  = substr( $method, 3 );
	
		if ( $firstBit == 'set' )
		{
			switch( $theRest )
			{
				case 'Order':
					$this->_sqlOrder = $arguments[0];
				break;
				case 'Where':
					$this->_sqlWhere = $arguments[0];
				break;
				case 'Select':
					$this->_sqlSelect = $arguments[0];
				break;
				case 'Limit':
					$this->_sqlLimit = $arguments[0];
				break;
				case 'Joins':
					$this->_sqlJoins = $arguments[0];
				break;
				case 'TblPrefix':
					$this->_sqlTblPrefix = $arguments[0];
				break;
			}
		}
		else if ( $firstBit == 'get' )
		{
			switch( $theRest )
			{
				case 'Order':
					return $this->_sqlOrder;
				break;
				case 'Where':
					return $this->_sqlWhere;
				break;
				case 'Select':
					return $this->_sqlSelect;
				break;
				case 'Limit':
					return $this->_sqlLimit;
				break;
				case 'Joins':
					return $this->_sqlJoins;
				break;
				case 'TblPrefix':
					return $this->_sqlTblPrefix;
				break;
			}
		}
		else
		{
			trigger_error( "Call to undefined function " . $method . " in classNodes.php" );
		}
	}
	
    /**
     * Fetches a single node's content
     *
     * @access	public
     * @param	int
     * @return	array
     */
    public function fetchNodeContent( $nodeId )
    {
    	$fetch    = ( is_numeric( $nodeId ) ) ? '=' . intval( $nodeId ) : ' IN (' . implode( ',', IPSLib::cleanIntArray( (array) $nodeId ) ) . ')';
    	$gotCache = array();
    	
    	if ( ! is_numeric( $nodeId ) )
    	{
    		foreach( $nodeId as $id )
    		{
    			if ( isset( $this->_nodeCache[ $id ] ) )
    			{
    				$gotCache[ $id ] = $this->_nodeCache[ $id ];
    			}
    		}
    		
    		if ( count( $gotCache ) == count( $nodeId ) )
    		{
    			return $gotCache;
    		}
    		else
    		{
    			$gotCache = array();
    		}
    	}
    	else
    	{
    		if ( isset( $this->_nodeCache[ $nodeId ] ) )
    		{
    			return $this->_nodeCache[ $nodeId ];
    		}
    	}
    	
    	/* Still here? */
    	if ( is_numeric( $nodeId ) OR ! count( $gotCache ) )
    	{
    		if ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) )
			{ 
				$this->DB->build( array( 'select'   => $this->_sqlTblPrefix . '.' . $this->_sqlSelect,
										 'from'     => array( $this->_sqlTable => $this->_sqlTblPrefix  ),
									 	 'where'    => $this->_sqlTblPrefix . '.' . $this->_sqlPrimaryID . $fetch,
										 'add_join' => $this->_sqlJoins  ) );
			}
			else
			{
				$this->DB->build( array( 'select'   => $this->_sqlSelect,
									 	 'from'     => $this->_sqlTable,
									 	 'where'    => $this->_sqlPrimaryID . $fetch ) );
			}
			
			$o = $this->DB->execute();
			
			while( $row = $this->DB->fetch( $o ) )
			{
				$return[ $row[ $this->_sqlPrimaryID ] ]           = $row;
				$this->_nodeCache[ $row[ $this->_sqlPrimaryID ] ] = $row;
			}
    	}
    	
    	return ( is_numeric( $nodeId ) ) ? $this->_nodeCache[ $nodeId ] : $return;
    }
    
    /**
     * Fetches left/right/level data
     *
     * @access	public
     * @param	int
     * @return	array - left, right, level
     */
    public function fetchNodeInfo( $nodeId )
    {
    	/* If this is ID 0... */
    	if ( $nodeId == 0 )
    	{
    		$left  = $this->DB->buildAndFetch( array( 'select' => 'MIN(' . $this->_sqlNodeLeft . ') as nleft',
    												  'from'   => $this->_sqlTable,
    												  'where'  => $this->_sqlNodeLevel . '=0' ) );
    		
    		$right = $this->DB->buildAndFetch( array( 'select' => 'MAX(' . $this->_sqlNodeRight . ') as nright',
    												  'from'   => $this->_sqlTable,
    												  'where'  => $this->_sqlNodeLevel . '=0' ) );
    		
    		return array( $left['nleft'], $right['nright'], 0 );
    	}
    	else
    	{
	    	$data = $this->fetchNodeContent( $nodeId );
	    	
	    	return array( $data[ $this->_sqlNodeLeft ],
	    				  $data[ $this->_sqlNodeRight ],
	    				  $data[ $this->_sqlNodeLevel ] );
    	}
    }

    /**
     * Fetches parent info
     *
     * @access	public
     * @param 	int Node id
     * @param 	string	Where data
     * @return 	DB row
    */
    public function fetchParentInfo( $nodeId, $where = '')
    {
    	/* Init where */
    	$where = ( $where ) ? $where : $this->_sqlWhere;
    	
    	/* Fetch noded data */
    	$nodeData = $this->fetchNodeInfo( $nodeId );
    	
    	if ( count( $nodeData ) )
    	{
    		return false;
    	}
    	
    	/* Lets go */
    	list( $leftId, $rightId, $level ) = $nodeData;
    	
    	/* Jump up a level */
    	$level--;
    	
    	$where = ( $where ) ? ' AND (' . $where . ')' : '';
    	
    	/* Fetch data */
    	if ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) )
		{
			$this->DB->build( array( 'select'   => $this->_sqlTblPrefix . '.' . $this->_sqlSelect,
									 'from'     => array( $this->_sqlTable => $this->_sqlTblPrefix  ),
								 	 'where'    => $this->_sqlTblPrefix . '.' . $this->_sqlNodeLeft . ' < ' . $leftId .
								 	 			   ' AND ' . $this->_sqlTblPrefix . '.' . $this->_sqlNodeLeft . ' > ' . $rightId .
								 	 			   ' AND ' . $this->_sqlTblPrefix . '.' . $this->_sqlNodeLevel . ' = ' . $level .
								 	 			   $where,
								 	 'order'    => $this->_sqlTblPrefix . '.' . $this->_sqlNodeLeft,
									 'add_join' => $this->_sqlJoins  ) );
		}
		else
		{
			$this->DB->build( array( 'select'   => $this->_sqlSelect,
								 	 'from'     => $this->_sqlTable,
								 	 'where'    => $this->_sqlNodeLeft . ' < ' . $leftId .
								 	 			   ' AND ' . $this->_sqlNodeLeft . ' > ' . $rightId .
								 	 			   ' AND ' . $this->_sqlNodeLevel . ' = ' . $level .
								 	 			   $where,
								 	 'order'    => $this->_sqlNodeLeft ) );
		}
		
		$o = $this->DB->execute();
    
    	$this->_nodeCache[ $nodeId ] = $row = $this->DB->fetch( $o );
    	
    	return ( count( $row ) ) ? $row : false;
    }

	/**
     * Returns DB resource to all nodes
     *
     * @access	public
     * @param	string		Where string
     * @return   boolean
    */
    public function fetchTree( $where='' )
    {
    	/* Init where */
    	$where = ( $where ) ? $where : $this->_sqlWhere;
    	
    	/* Fetch data */
    	if ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) OR ! empty( $this->_sqlTblPrefix ) )
		{
			$_order = $this->getOrder();
			$order  = ( ! empty( $_order ) ) ? $_order : $this->_sqlTblPrefix . '.' . $this->_sqlNodeLeft;
			
			$this->DB->build( array( 'select'   => $this->_sqlTblPrefix . '.' . $this->_sqlSelect,
									 'from'     => array( $this->_sqlTable => $this->_sqlTblPrefix  ),
									 'where'    => $where,
									 'limit'    => $this->getLimit(),
								 	 'order'    => $order,
									 'add_join' => $this->_sqlJoins  ) );
		}
		else
		{
			$_order = $this->getOrder();
			$order  = ( ! empty( $_order ) ) ? $_order : $this->_sqlNodeLeft;
			
			$this->DB->build( array( 'select'   => $this->_sqlSelect,
								 	 'from'     => $this->_sqlTable,
								 	 'where'    => $where,
									 'limit'    => $this->getLimit(),
								 	 'order'    => $order ) );
		}
    
    	/* Set up handler */
    	$this->_dbHandle = $this->DB->execute();
    	
        return true;
    }
    
    /**
     * Fetch the branch starting with $nodeId
     *
     * @access	public
     * @param	int		Node Id
     * @param	string	Where clause
     * @return 	boolean
     */
	public function fetchBranch( $nodeId, $inWhere='' )
	{
		/* Init where */
    	$inWhere = ( $inWhere ) ? $inWhere : $this->_sqlWhere;
    	
		/* Set up tbl prefix */
		$a = ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) ) ? $this->_sqlTblPrefix : 'aaa';
		
		/* Set up select */
		$select = $a . '.' . $this->_sqlSelect;
		
		$where  = 'bbb.' . $this->_sqlPrimaryID . '=' . $nodeId . ' AND ' . $a . '.' . $this->_sqlNodeLeft . ' >= bbb.' . $this->_sqlNodeLeft . ' AND ' . $a . '.' . $this->_sqlNodeRight . ' <= bbb.' . $this->_sqlNodeRight;
												  
        /* Custom where? */
        $where .= ( $inWhere ) ? ' AND (' . $inWhere . ')' : '';
        
         $_joins   = $this->_sqlJoins;
         $_joins[] = array( 'from' => array( $this->_sqlTable => 'bbb' ),
        				    'type' => 'inner' );
        
        $this->DB->build( array( 'select'   => $select,
								 'from'     => array( $this->_sqlTable => $a  ),
								 'where'    => $where,
								 'order'    => $a . '.' . $this->_sqlNodeLeft,
								 'add_join' => $_joins  ) );
		
        
        /* Set up handler */
    	$this->_dbHandle = $this->DB->execute();
    	
        return true;
    }

	/**
     * Fetch the parents of $nodeId
     *
     * @access	public
     * @param	int		Node Id
     * @param	string	Where clause
     * @return 	boolean
     */
	public function fetchParents( $nodeId, $inWhere='' )
	{
		/* Init where */
    	$inWhere = ( $inWhere ) ? $inWhere : $this->_sqlWhere;
    	
		/* Set up tbl prefix */
		$a = ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) ) ? $this->_sqlTblPrefix : 'aaa';
		
		/* Set up select */
		$select = $a . '.' . $this->_sqlSelect;
		
		$where  = 'bbb.' . $this->_sqlPrimaryID . '=' . $nodeId . ' AND bbb.' . $this->_sqlNodeLeft . ' BETWEEN ' . $a . '.' . $this->_sqlNodeLeft . ' AND ' . $a . '.' . $this->_sqlNodeRight;
												  
        /* Custom where? */
        $where .= ( $inWhere ) ? ' AND (' . $inWhere . ')' : '';
        
        $_joins   = $this->_sqlJoins;
        $_joins[] = array( 'from' => array( $this->_sqlTable => 'bbb' ),
        				   'type' => 'inner' );
        
        $this->DB->build( array( 'select'   => $select,
								 'from'     => array( $this->_sqlTable => $a  ),
								 'where'    => $where,
								 'order'    => $a . '.' . $this->_sqlNodeLeft,
								 'add_join' => $_joins  ) );
		
        
        /* Set up handler */
    	$this->_dbHandle = $this->DB->execute();
    	
        return true;
    }
    
	/**
     * Fetch the children of $nodeId
     *
     * @access	public
     * @param	int		Node Id
     * @param	string	Where clause
     * @return 	boolean
     */
	public function fetchChildren( $nodeId, $inWhere='' )
	{
		/* Init where */
    	$inWhere = ( $inWhere ) ? $inWhere : $this->_sqlWhere;
    	
		/* Set up tbl prefix */
		$a = ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) ) ? $this->_sqlTblPrefix : 'aaa';
		
		/* Set up select */
		$select = $a . '.' . $this->_sqlSelect;
		
		if ( $nodeId == 0 )
		{
			$where = '(1=1)';
		}
		else
		{
			$where  = 'bbb.' . $this->_sqlPrimaryID . '=' . $nodeId . ' AND ' . $a . '.' .  $this->_sqlNodeLeft . ' > ' . 'bbb.' . $this->_sqlNodeLeft . ' AND ' . $a . '.' .  $this->_sqlNodeRight . ' < bbb.' . $this->_sqlNodeRight;
		}
												  
        /* Custom where? */
        $where .= ( $inWhere ) ? ' AND (' . $inWhere . ')' : '';
        
        $_joins   = $this->_sqlJoins;
        
        if ( $nodeId > 0 )
		{
	        $_joins[] = array( 'from' => array( $this->_sqlTable => 'bbb' ),
	        				   'type' => 'inner' );
		}
        
        $this->DB->build( array( 'select'   => $select,
								 'from'     => array( $this->_sqlTable => $a  ),
								 'where'    => $where,
								 'order'    => $a . '.' . $this->_sqlNodeLeft,
								 'limit'    => $this->getLimit(),
								 'add_join' => $_joins  ) );
		
        
        /* Set up handler */
    	$this->_dbHandle = $this->DB->execute();
    	
        return true;
    }

    /**
     * Returns an item with parents and children
     *
     * @access	public
     * @param	int		Node Id
     * @param	string	Where clause
     * @return 	boolean
     */
    public function fetchSlice( $nodeId, $inWhere='' )
    {
    	/* Init where */
    	$inWhere = ( $inWhere ) ? $inWhere : $this->_sqlWhere;
    	
    	/* Init */
    	$i = 0;
    	
    	/* Set up tbl prefix */
		$a = ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) ) ? $this->_sqlTblPrefix : 'aaa';
		
		/* Select */
		$select = $a . '.' . $this->_sqlNodeLeft . ', ' .  $a . '.' . $this->_sqlNodeRight . ', ' . $a . '.' . $this->_sqlNodeLevel;	
		$where  = 'bbb.' . $this->_sqlPrimaryID . '=' . $nodeId . ' AND bbb.' . $this->_sqlNodeLeft . ' BETWEEN ' . $a . '.' . $this->_sqlNodeLeft . ' AND ' . $a . '.' . $this->_sqlNodeRight;
												  
        /* Custom where? */
        $where .= ( $inWhere ) ? ' AND (' . $inWhere . ')' : '';
        
        $_joins   = $this->_sqlJoins;
        $_joins[] = array( 'from' => array( $this->_sqlTable => 'bbb' ),
        				   'type' => 'inner' );
        
        $this->DB->build( array( 'select'   => $select,
								 'from'     => array( $this->_sqlTable => $a  ),
								 'where'    => $where,
								 'order'    => $a . '.' . $this->_sqlNodeLeft,
								 'add_join' => $_joins  ) );
		
        
        /* Set up handler */
    	$this->_dbHandle = $this->DB->execute();
    	
    	$len = $this->recordCount();
    	
    	/* Set up tbl prefix */
		$a = ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) ) ? $this->_sqlTblPrefix : 'aaa';
		
		$_where = ( $inWhere ) ? ' AND (' . $inWhere . ')' : '';
		
		/* start main where */
		$where = '(' . $a . '.' . $this->_sqlNodeLevel . ' =1';
		
		while( $row = $this->DB->fetch( $this->_dbHandle ) )
		{
			if ( ( ++$i == $len ) && ( $row[ $this->_sqlNodeLeft ] + 1 ) == $row[ $this->_sqlNodeRight ] )
			{
                break;
            }
            
            $where .= ' OR (' . $a .'.'. $this->_sqlNodeLevel . ' = ' . ( $row[$this->_sqlNodeLevel] + 1 )
            	   . ' AND '  . $a .'.'. $this->_sqlNodeLeft  . ' > ' .   $row[$this->_sqlNodeLeft]
            	   . ' AND '  . $a .'.'. $this->_sqlNodeRight . ' < ' .   $row[$this->_sqlNodeRight] . ')';
		} 
    	
    	$where .= ")" . $_where;
    	
    	/* Fetch data */
    	if ( is_array( $this->_sqlJoins ) and count( $this->_sqlJoins ) )
		{
			$this->DB->build( array( 'select'   => $this->_sqlTblPrefix . '.' . $this->_sqlSelect,
									 'from'     => array( $this->_sqlTable => $this->_sqlTblPrefix  ),
									 'where'    => $where,
								 	 'order'    => $this->_sqlTblPrefix . '.' . $this->_sqlNodeLeft,
									 'add_join' => $this->_sqlJoins  ) );
		}
		else
		{
			$this->DB->build( array( 'select'   => $a . '.' . $this->_sqlSelect,
								 	 'from'     => $this->_sqlTable . ' ' . $a,
								 	 'where'    => $where,
								 	 'order'    => $a . '.' . $this->_sqlNodeLeft ) );
		}
		
		/* Set up handler */
    	$this->_dbHandle = $this->DB->execute();
    	
        return true;
    }
    
    /**
     * Is descendant of
     * Checks to see if a node is a descendant of another node. Simple really
     * @param	array	node (descendant)
     * @param	array	node (root parent)
     */
    public function isDescendantOf( $desc, $parent )
    {
    	if ( ! isset( $desc[ $this->_sqlNodeLeft ] ) && ! isset( $desc[ $this->_sqlNodeRight ] ) )
    	{
    		return false;
    	}
    	
    	if ( ! isset( $parent[ $this->_sqlNodeLeft ] ) && ! isset( $parent[ $this->_sqlNodeRight ] ) )
    	{
    		return false;
    	}
    	
    	if ( $desc[ $this->_sqlNodeLeft ] > $parent[ $this->_sqlNodeLeft ] && $desc[ $this->_sqlNodeLeft ] < $parent[ $this->_sqlNodeRight ] )
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    /**
     * Add node
     *
     * @param	int		Parent Node ID
     * @param	array	Array of fields to insert
     * @return	int		Insert ID
     */
    public function addNode( $parentId, $data )
    {
    	$nInfo = $this->fetchNodeInfo( $parentId );
    	
    	if ( ! isset( $nInfo[0] ) OR empty( $parentId ) )
    	{
    		 /* Add without parents? */
    		 if ( empty( $parentId ) )
    		 {
    		 	/* Find the highest right value */
    		 	$right = $this->DB->buildAndFetch( array( 'select'   => '*',
									 	 				  'from'     => $this->_sqlTable,
									 	 				  'order'    => $this->_sqlNodeRight . ' DESC',
    		 											  'limit'    => array( 0, 1 ) ) );
    		 	$leftId  = $right[ $this->_sqlNodeRight ] + 1;
    		 	$rightId = $right[ $this->_sqlNodeRight ] + 2;
    		 	$level	 = 0;
			
	        	$data[ $this->_sqlNodeLeft ]  = $leftId;
	        	$data[ $this->_sqlNodeRight ] = $rightId;
	        	$data[ $this->_sqlNodeLevel ] = $level;
    		 }
    		 else
    		 {
    		 	return false;
    		 }
    	}
    	else
    	{
			list( $leftId, $rightId, $level ) = $nInfo;
			
	        $data[ $this->_sqlNodeLeft ]  = $rightId;
	        $data[ $this->_sqlNodeRight ] = ( $rightId + 1 );
	        $data[ $this->_sqlNodeLevel ] = ( $level + 1 );
	        
	        /* Set data: Shift over the elements */
			$set = $this->_sqlNodeLeft .  ' = CASE WHEN ' . $this->_sqlNodeLeft  . '>'  . $rightId . ' THEN ' . $this->_sqlNodeLeft  . '+2 ELSE ' . $this->_sqlNodeLeft  . ' END, '
				 . $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeRight . '>=' . $rightId . ' THEN ' . $this->_sqlNodeRight . '+2 ELSE ' . $this->_sqlNodeRight . ' END ';
			
			/* Pre formatted */
			$this->DB->update( $this->_sqlTable, $set, $this->_sqlNodeRight . '>=' . $rightId, false, true );
    	}	
        
        /* Insert new row */
        $this->DB->insert( $this->_sqlTable, $data );
      	
        return $this->DB->getInsertId();
    }
   
    /**
     * Move and node and all children to a new parent node
     *
     * @param	int		Node ID
     * @param	int		New parent ID
     * @return	int		Insert ID
     */
    public function moveNode( $nodeId, $newParentId )
    {
    	$nInfo = $this->fetchNodeInfo( $nodeId );
    	
    	if ( ! isset( $nInfo[0] ) )
    	{
    		return false;
    	}
    	
    	list( $leftId, $rightId, $level ) = $nInfo;
    	
    	$nInfo = $this->fetchNodeInfo( $newParentId );
    	
    	if ( ! isset( $nInfo[0] ) )
    	{
    		return false;
    	}
    	
    	list( $leftIdP, $rightIdP, $levelP ) = $nInfo;
    	
		/* Make sure it can't be moved into itself or into an invalid node */
        if ( $nodeId == $newParentId || $leftId == $leftIdP || ( $leftIdP >= $leftId && $leftIdP <= $rightId ) || ( $newParentId > 0 && $level == $levelP + 1 && $leftId > $leftIdP && $rightId < $rightIdP ) )
        {
            return false;
        }
     
    	if ( $newParentId == 0 )
        { 
        	/* set the math right */
        	$levelP = -1;
        	
            $set = $this->_sqlNodeLevel . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLevel . sprintf( '%+d', - ( $level - 1 ) + $levelP ) . ' ELSE ' . $this->_sqlNodeLevel . ' END, '
                 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $rightId . ' AND ' . $rightIdP . ' THEN ' . $this->_sqlNodeLeft . '-' . ( $rightId - $leftId + 1 ) . ' '
                 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLeft . '+' . ( $rightIdP - $rightId ) . ' ELSE ' . $this->_sqlNodeLeft . ' END, '
                 . $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . ( $rightId + 1 ) . ' AND ' . ( $rightIdP ) . ' THEN ' . $this->_sqlNodeRight . '-' . ( $rightId - $leftId + 1 ) . ' '
                 . 'WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeRight . '+' . ( $rightIdP - $rightId ) . ' ELSE ' . $this->_sqlNodeRight . ' END ';
           
            $whr = '(' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ' ' . 'OR ' . $this->_sqlNodeRight . ' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ')';
        }
        else if ( $leftIdP < $leftId && $rightIdP > $rightId && ( $levelP < $level - 1 ) )
        {
            $set = $this->_sqlNodeLevel . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLevel . sprintf('%+d', - ( $level - 1) + $levelP ) . ' ELSE ' . $this->_sqlNodeLevel . ' END, '
                 . $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . ( $rightId + 1 ) . ' AND ' . ( $rightIdP - 1 ) . ' THEN ' . $this->_sqlNodeRight . '-' . ( $rightId - $leftId + 1) . ' '
                 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeRight . '+' . ( ( ( $rightIdP - $rightId - $level + $levelP ) / 2 ) * 2 + $level - $levelP - 1 ) . ' ELSE ' . $this->_sqlNodeRight . ' END, '
                 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . ( $rightId + 1 ) . ' AND ' . ( $rightIdP - 1 ) . ' THEN ' . $this->_sqlNodeLeft . '-' . ( $rightId - $leftId + 1 ) . ' '
                 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLeft . '+' . ( ( ( $rightIdP - $rightId - $level + $levelP ) / 2 ) * 2 + $level - $levelP - 1 ) . ' ELSE ' . $this->_sqlNodeLeft . ' END ';
            
            $whr = $this->_sqlNodeLeft . ' BETWEEN ' . ( $leftIdP + 1 ) . ' AND ' . ( $rightIdP - 1 );
        }
        else if ( $leftIdP < $leftId )
        {
            $set = $this->_sqlNodeLevel . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLevel . sprintf( '%+d', - ( $level - 1 ) + $levelP ) . ' ELSE ' . $this->_sqlNodeLevel . ' END, '
	             . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $rightIdP . ' AND ' . ( $leftId - 1 ) . ' THEN ' . $this->_sqlNodeLeft . '+' . ( $rightId - $leftId + 1 ) . ' '
	             . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLeft . '-' . ( $leftId - $rightIdP ) . ' ELSE ' . $this->_sqlNodeLeft . ' END, '
	             . $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . $rightIdP . ' AND ' . $leftId . ' THEN ' . $this->_sqlNodeRight . '+' . ( $rightId - $leftId + 1) . ' '
	             . 'WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeRight . '-' . ( $leftId - $rightIdP ) . ' ELSE ' . $this->_sqlNodeRight . ' END ';
	        
	        $whr = '(' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftIdP . ' AND ' . $rightId. ' ' . 'OR ' . $this->_sqlNodeRight . ' BETWEEN ' . $leftIdP . ' AND ' . $rightId . ')';
        }
        else
        { 
            $set = $this->_sqlNodeLevel . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLevel . sprintf( '%+d', - ( $level - 1 ) + $levelP ) . ' ELSE ' . $this->_sqlNodeLevel . ' END, '
                 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $rightId . ' AND ' . $rightIdP . ' THEN ' . $this->_sqlNodeLeft . '-' . ( $rightId - $leftId + 1 ) . ' '
                 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLeft . '+' . ( $rightIdP - 1 - $rightId ) . ' ELSE ' . $this->_sqlNodeLeft . ' END, '
                 . $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . ( $rightId + 1 ) . ' AND ' . ( $rightIdP - 1 ) . ' THEN ' . $this->_sqlNodeRight . '-' . ( $rightId - $leftId + 1 ) . ' '
                 . 'WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeRight . '+' . ( $rightIdP - 1 - $rightId ) . ' ELSE ' . $this->_sqlNodeRight . ' END ';
           
            $whr = '(' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ' ' . 'OR ' . $this->_sqlNodeRight . ' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ')';
        }
      	
        //print "<pre>";
        //print wordwrap( $set, 100, '<br />' );
        //print "\n" . wordwrap( $whr, 100, '<br />' );
        //exit();
        /* Pre formatted */
		$this->DB->update( $this->_sqlTable, $set, $whr, false, true );
		
        return true;
    }
    
    
	/**
     * Delete a node. Any children are moved to root.
     *
     * @param	int		Node ID
     * @return	boolean
     */
	public function deleteNode( $nodeId )
	{
		$nInfo = $this->fetchNodeInfo( $nodeId );
    	
		if ( ! isset( $nInfo[0] ) )
    	{
    		return false;
    	}
    	
    	list( $leftId, $rightId, $level ) = $nInfo;
    	
    	/* Delete from DB */
    	$this->DB->delete( $this->_sqlTable, $this->_sqlPrimaryID . '=' . intval( $nodeId ) );

	    /* Move other nodes down */
	    $set = $this->_sqlNodeLevel . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLevel . ' - 1 ELSE ' . $this->_sqlNodeLevel . ' END, '
	     	 . $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeRight . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeRight . ' - 1 '
	     	 . 'WHEN ' . $this->_sqlNodeRight . ' > ' . $rightId . ' THEN ' . $this->_sqlNodeRight . ' - 2 ELSE ' . $this->_sqlNodeRight . ' END, '
	     	 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN ' . $this->_sqlNodeLeft . ' - 1 '
	     	 . 'WHEN ' . $this->_sqlNodeLeft . ' > ' . $rightId . ' THEN ' . $this->_sqlNodeLeft . ' - 2 ELSE ' . $this->_sqlNodeLeft . ' END ';
	    
	    $whr = $this->_sqlNodeRight . ' > ' . $leftId;
	     
		/* Pre formatted */
		$this->DB->update( $this->_sqlTable, $set, $whr, false, true );
		
        return true;
	 }
	
	/**
     * Delete a node branch (node + children)
     *
     * @param	int		Node ID
     * @return	boolean
     */
	public function deleteBranch( $nodeId )
	{
		$nInfo = $this->fetchNodeInfo( $nodeId );
    	
		if ( ! isset( $nInfo[0] ) )
    	{
    		return false;
    	}
    	
    	list( $leftId, $rightId, $level ) = $nInfo;
    	
    	/* Delete from DB */
    	$this->DB->delete( $this->_sqlTable, $this->_sqlNodeLeft . ' BETWEEN ' . $leftId . ' AND ' . $rightId );
	
		/* Fix position */    
		$deltaId = ( ( $rightId - $leftId ) + 1 );
		
		$set = $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' > ' . $leftId.' THEN ' . $this->_sqlNodeLeft . ' - ' . $deltaId . ' ELSE ' . $this->_sqlNodeLeft . ' END, '
	     	 . $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeRight . ' > ' . $leftId . ' THEN ' . $this->_sqlNodeRight . ' - ' . $deltaId . ' ELSE ' . $this->_sqlNodeRight . ' END ';
		
		$whr = $this->_sqlNodeRight . ' > ' . $rightId;
	    
	    /* Pre formatted */
		$this->DB->update( $this->_sqlTable, $set, $whr, false, true );
		
		return true;
	 }

    /**
     * Switch a node's position within it's level
     *
     * @access	public
     * @param	int		ID1
     * @param	int		ID 2
     * @param	string	position (before/after)
     * @return 	boolean
     */
    public function movePosition( $id1, $id2, $position='after')
    {
    	$nInfo = $this->fetchNodeInfo( $id1 );
    	
    	if ( ! isset( $nInfo[0] ) )
    	{
    		return false;
    	}
    	
    	list( $leftId1, $rightId1, $level1 ) = $nInfo;
    	
    	$nInfo = $this->fetchNodeInfo( $id2 );
    	
    	if ( ! isset( $nInfo[0] ) )
    	{
    		return false;
    	}
    	
    	list( $leftId2, $rightId2, $level2 ) = $nInfo;
    	
    	/* On the level? */
    	if ( $level1 != $level2 )
    	{
    		return false;
    	}
    	
        /* Before? */
        if ( $position == 'before' )
        {
            if ( $leftId1 > $leftId2 )
            {
                $set = $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeRight . ' - ' . ($leftId1 - $leftId2) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId2 . ' AND ' . ($leftId1 - 1) . ' THEN ' . $this->_sqlNodeRight . ' +  ' . ( $rightId1 - $leftId1 + 1 ) . ' ELSE ' . $this->_sqlNodeRight . ' END, '
                	 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeLeft . ' - ' . ($leftId1 - $leftId2) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId2 . ' AND ' . ($leftId1 - 1) . ' THEN ' . $this->_sqlNodeLeft . ' + ' . ( $rightId1 - $leftId1 + 1 ) . ' ELSE ' . $this->_sqlNodeLeft . ' END ';
                
                $whr = $this->_sqlNodeLeft . ' BETWEEN ' . $leftId2 . ' AND ' . $rightId1;
            }
            else
            {
                $set = $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeRight . ' + ' . ( ( $leftId2 - $leftId1 ) - ( $rightId1 - $leftId1 + 1 ) ) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . ( $rightId1 + 1 ) . ' AND ' . ($leftId2 - 1) . ' THEN ' . $this->_sqlNodeRight . ' - ' . ( ( $rightId1 - $leftId1 + 1 ) ) . ' ELSE ' . $this->_sqlNodeRight . ' END, '
                	 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeLeft . ' + ' . ( ( $leftId2 - $leftId1 ) - ( $rightId1 - $leftId1 + 1 ) ) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . ( $rightId1 + 1 ) . ' AND ' . ($leftId2 - 1) . ' THEN ' . $this->_sqlNodeLeft . ' - ' . ( $rightId1 - $leftId1 + 1 ) . ' ELSE ' . $this->_sqlNodeLeft . ' END ';
                
                $whr = $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . ($leftId2 - 1);
            }
        }
        
        if ( $position == 'after' )
        {
            if ( $leftId1 > $leftId2 )
            {
                $set = $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeRight . ' - ' . ( $leftId1 - $leftId2 - ( $rightId2 - $leftId2 + 1 ) ) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . ( $rightId2 + 1 ) . ' AND ' . ( $leftId1 - 1 ) . ' THEN ' . $this->_sqlNodeRight . ' +  ' . ( $rightId1 - $leftId1 + 1) . ' ELSE ' . $this->_sqlNodeRight . ' END, '
                	 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeLeft . ' - ' . ($leftId1 - $leftId2 - ( $rightId2 - $leftId2 + 1 ) ) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . ( $rightId2 + 1 ) . ' AND ' . ( $leftId1 - 1 ) . ' THEN ' . $this->_sqlNodeLeft . ' + ' . ( $rightId1 - $leftId1 + 1) . ' ELSE ' . $this->_sqlNodeLeft . ' END ';
                
                $whr = $this->_sqlNodeLeft . ' BETWEEN ' . ($rightId2 + 1) . ' AND ' . $rightId1;
            }
            else
            {
                $set = $this->_sqlNodeRight . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeRight . ' + ' . ($rightId2 - $rightId1) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . ( $rightId1 + 1 ) . ' AND ' . $rightId2 . ' THEN ' . $this->_sqlNodeRight . ' - ' . ( ( $rightId1 - $leftId1 + 1 ) ) . ' ELSE ' . $this->_sqlNodeRight . ' END, '
                	 . $this->_sqlNodeLeft . ' = CASE WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId1 . ' THEN ' . $this->_sqlNodeLeft . ' + ' . ($rightId2 - $rightId1) . ' '
                	 . 'WHEN ' . $this->_sqlNodeLeft . ' BETWEEN ' . ( $rightId1 + 1 ) . ' AND ' . $rightId2 . ' THEN ' . $this->_sqlNodeLeft . ' - ' . ( $rightId1 - $leftId1 + 1 ) . ' ELSE ' . $this->_sqlNodeLeft . ' END ';
                
                $where = $this->_sqlNodeLeft . ' BETWEEN ' . $leftId1 . ' AND ' . $rightId2;
            }
        }
        
        /* Pre formatted */
		$this->DB->update( $this->_sqlTable, $set, $whr, false, true );
		
        return true;
    }
    
    
    /**
    * Returns record count
    *
    * @return int
    */
    public function recordCount()
    {
        return $this->DB->getTotalRows( $this->_dbHandle );
    }

    /**
    * Returns the current row.
    *
    * @return array
    */
    public function nextRow()
    {
        return $this->DB->fetch( $this->_dbHandle );
    }
    
    /**
     * Rebuild the tree
     *
     * @access	public
     * @param	int		Root ID
     * @param	int		Left value
     * @param	int		Depth
     * @return	nothing
     */
    function rebuildTree( $root=0, $left=1, $depth=-1 )
    {
    	$right  = $left + 1;
    	$nDepth = $depth + 1;
    	
    	$this->DB->build( array( 'select' => '*',
    							 'from'   => $this->_sqlTable,
    							 'where'  => $this->_sqlParentID . '=' . intval( $root ),
    							 'order'  => $this->_sqlOrder ) );
    							 
    	$o = $this->DB->execute();
    	
    	while( $row = $this->DB->fetch( $o ) )
    	{
    		$right = $this->rebuildTree( $row[ $this->_sqlPrimaryID ], $right, $nDepth );
    	} 
    	
    	$this->DB->update( $this->_sqlTable, array( $this->_sqlNodeLeft  => $left,
    												$this->_sqlNodeRight => $right,
    												$this->_sqlNodeLevel => $depth ), $this->_sqlPrimaryID . '=' . $root );
    												
    	return $right + 1;
    }
    
	/**
	 * Drop the node cache
	 * 
	 */
	public function dropCache()
	{
		$this->_nodeCache = array();
	}
	
    /**
     * Show the entire tree as HITMUHL
     *
     * @access	public
     */
    public function debugShowTree()
    {
    	/* Init */
    	$content = '';
    	
    	/* reset params */
    	$this->setOrder(null);
    	$this->setWhere(null);
    	
    	/* Load entire tree */
    	$this->fetchTree();
    	
    	while( $item = $this->nextRow() )
    	{
        	$content .= str_repeat( '&nbsp;', 6 * $item[ $this->_sqlNodeLevel ] ) . '(' . $item[ $this->_sqlPrimaryID ] . ')<strong>' . $item[ $this->_sqlTitle ] . '</strong><br />';
        }
        
        return $content;
    }
}

/**
 * Static class of functions for post processing node data
 */
class nodeFunctions
{
	/**
	 * Create a nestled array of data from a tree or branch
	 *
	 * @access	public
	 * @param	array		array of data from classNodes::fetch*
	 * @param	string		Field name for 'right'
	 * @return	Array		
	 */
	static public function nestle( array $array, $nDepthField='level' )
	{
		/* Init */
		$nested = array();
        $depths = array();
		
        foreach( $array as $key => $data )
        {
            if ( $data[ $nDepthField ] == 0 )
            {
                $nested[ $key ] = $data;
                $depths[ $data[ $nDepthField ] + 1] = $key;
            }
            else
            {
                $parent =& $nested;
                
                for( $i = 1; $i <= ( $data[ $nDepthField ] ); $i++ )
                {
                    $parent =& $parent[ $depths[ $i ] ];
                }

                $parent['_children_'][ $key ] = $data;
                $depths[ $data[ $nDepthField ] + 1 ] = $key;
            }
        }

        return $nested;
	}

}