package flare.query
{    
    /**
     * The ExpressionIterator simplifies the process of traversing an
     * expression tree.
     */
    public class ExpressionIterator
    {
        private var _estack:Array;
        private var _istack:Array;
        private var _root:Expression;
        private var _cur:Expression;
        private var _idx:int;
        
        /** The expression being traversed. */
        public function get expression():Expression { return _root; }
        public function set expression(expr:Expression):void {
            _root = expr; reset();
        }
        /** The parent expression of the iterator's current position. */
        public function get parent():Expression { return _estack[_estack.length-1]; }
        /** The expression at this iterator's current position. */
        public function get current():Expression { return _cur; }
        /** The depth of this iterator's current position in the
         *  expression tree. */
        public function get depth():int { return _estack.length; }
        /** An array of expressions from the root expression down to this
         *  iterator's current position. */
        public function get path():Array {
            var a:Array = new Array(_estack.length);
            for (var i:int=0; i<a.length; ++i) { a[i] = _estack[i]; }
            a.push(_cur);
            return a;
        }
        
        /**
         * Creates a new ExpressionIterator.
         * @param expr the expression to iterate over
         */
        public function ExpressionIterator(expr:Expression=null) {
            expression = expr;
        }
        
        /**
         * Resets this iterator to the root of the expression tree.
         */
        public function reset():void
        {
            _estack = new Array();
            _istack = new Array();
            _cur = _root;
            _idx = 0;
        }
        
        /**
         * Moves the iterator one level up the expression tree.
         * @return the new current expression, or null if the iterator
         *  could not move any further up the tree
         */
        public function up():Expression
        {
            if (_cur != _root) {
                _cur = _estack.pop();
                _idx = _istack.pop();
                return _cur;
            } else {
                return null;
            }
        }
        
        /**
         * Moves the iterator one level down the expression tree. By default,
         * the iterator moves to the left-most child of the previous position.
         * @param idx the index of the child expression this iterator should
         *  move down to
         * @return the new current expression, or null if the iterator
         *  could not move any further down the tree
         */
        public function down(idx:int=0):Expression
        {
            if (idx >= 0 && _cur.numChildren > idx) {
                _estack.push(_cur);
                _istack.push(_idx);
                _idx = idx;
                _cur = _cur.getChildAt(_idx);
                return _cur;
            } else {
                return null;
            }
        }
        
        /**
         * Moves the iterator to the next sibling expression in the expression
         * tree.
         * @return the new current expression, or null if the current position
         *  has no next sibling.
         */
        public function next():Expression
        {
            if (_estack.length > 0) {
                var p:Expression = _estack[_estack.length-1];
                if (p.numChildren > _idx+1) {
                    _idx = _idx + 1;
                    _cur = p.getChildAt(_idx);
                    return _cur;
                }
            }
            return null;
        }
        
        /**
         * Moves the iterator to the previous sibling expression in the
         * expression tree.
         * @return the new current expression, or null if the current position
         *  has no previous sibling.
         */
        public function prev():Expression
        {
            if (_estack.length > 0) {
                var p:Expression = _estack[_estack.length-1];
                if (_idx > 0) {
                    _idx = _idx - 1;
                    _cur = p.getChildAt(_idx);
                    return _cur;
                }
            }
            return null;
        }
        
    } // end of class ExpressionIterator
}