package flare.query
{
    import flash.utils.ByteArray;
    
    /**
     * Base class for expressions with an arbitrary number of sub-expressions.
     */
    public class CompositeExpression extends Expression
    {
        /** Array of sub-expressions. */
        protected var _children:Array;
        
        /**
         * @inheritDoc
         */
        public override function get numChildren():int {
            return _children.length;
        }
        
        /**
         * Creates a new CompositeExpression.
         * @param items either a single sub-expression or an array of
         *  sub-expressions
         */
        public function CompositeExpression(items:Object=null) {
            if (items is Array) {
                setChildren(items as Array);
            } else if (items is Expression) {
                _children = new Array();
                addChild(items as Expression);
            } else if (items == null) {
                _children = new Array();
            } else {
                throw new ArgumentError(
                    "Input must be an expression or array of expressions");
            }
        }
        
        /**
         * Helper routine that clones this composite's sub-expressions.
         * @param ce the cloned composite expression
         * @return the input expression
         */
        protected function cloneHelper(ce:CompositeExpression):Expression
        {
            for (var i:int=0; i<_children.length; ++i) {
                ce.addChild(Expression(_children[i]).clone());
            }
            return ce;
        }
        
        /**
         * Sets the sub-expressions of this composite
         * @param array an array of sub-expressions
         */
        public function setChildren(array:Array):void
        {
            _children = new Array();
            for each (var e:* in array) {
                _children.push(Expression.expr(e));
            }
        }
        
        /**
         * Adds an additional sub-expression to this composite.
         * @param expr the sub-expression to add.
         */
        public function addChild(expr:Expression):void
        {
            _children.push(expr);
        }
        
        /**
         * Removes a sub-expression from this composite.
         * @param expr the sub-epxressions to remove
         * @return true if the expression was found and removed, false
         *  otherwise
         */
        public function removeChild(expr:Expression):Boolean
        {
            var idx:int = _children.indexOf(expr);
            if (idx >= 0) {
                _children.splice(idx, 1);
                return true;
            } else {
                return false;
            }
        }
        
        /**
         * @inheritDoc
         */
        public override function getChildAt(idx:int):Expression
        {
            return _children[idx];
        }
        
        /**
         * @inheritDoc
         */
        public override function setChildAt(idx:int, expr:Expression):Boolean
        {
            if (idx>=0 && idx<_children.length) {
                _children[idx] = expr;
                return true;
            }
            return false;
        }
        
        /**
         * Removes all sub-expressions from this composite.
         */
        public function removeAllChildren():void
        {
            while (_children.length > 0) _children.pop();
        }
        
        /**
         * Returns a string representation of this composite's sub-expressions.
         * @param op a string describing the sub-class operator (null by
         *  default). If non-null, the operator string will be interspersed
         *  between sub-expression values in the output string.
         * @return the requested string
         */
        protected function getString(op:String=null):String
        {            
            var b:ByteArray = new ByteArray();
            b.writeUTFBytes('(');
            for (var i:uint=0; i<_children.length; ++i) {
                if (i > 0) {
                    if (op == null) {
                        b.writeUTFBytes(', ');
                    } else {
                        b.writeUTFBytes(' ');
                        b.writeUTFBytes(op);
                        b.writeUTFBytes(' ');
                    }
                }
                b.writeUTFBytes(_children[i].toString());
            }
            b.writeUTFBytes(')');            

            b.position = 0;
            return b.readUTFBytes(b.length);
        }
        
    } // end of class CompositeExpression
}