package flare.vis.operator
{
    import flare.animate.Transitioner;
    import flare.util.Arrays;
    import flare.vis.Visualization;
    
    import flash.utils.flash_proxy;
    import flash.utils.Proxy;
    
    /**
     * An OperatorList maintains a sequential chain of operators that are
     * invoked one after the other. Operators can be added to an OperatorList
     * using the <code>add</code> method. Once added, operators can be
     * retrieved and set using their index in the lists, either with array
     * notation (<code>[]</code>) or with the <code>getOperatorAt</code> and
     * <code>setOperatorAt</code> methods.
     */
    public class OperatorList extends Proxy implements IOperator
    {
        // -- Properties ------------------------------------------------------
        
        protected var _vis:Visualization;
        protected var _enabled:Boolean = true;
        protected var _list:Array = new Array();
        
        /** The visualization processed by this operator. */
        public function get visualization():Visualization { return _vis; }
        public function set visualization(v:Visualization):void
        {
            _vis = v; setup();
            for each (var op:IOperator in _list) {
                op.visualization = v;
            }
        }
        
        /** Indicates if the operator is enabled or disabled. */
        public function get enabled():Boolean { return _enabled; }
        public function set enabled(b:Boolean):void { _enabled = b; }
        
        /** @inheritDoc */
        public function set parameters(params:Object):void
        {
            Operator.applyParameters(this, params);
        }
        
        /** An array of the operators contained in the operator list. */
        public function set list(ops:Array):void {
            // first remove all current operators
            while (_list.length > 0) {
                removeOperatorAt(_list.length-1);
            }
            // then add the new operators
            for each (var op:IOperator in ops) {
                add(op);
            }
        }
        
        /** The number of operators in the list. */
        public function get length():uint { return _list.length; }
        /** Returns the first operator in the list. */
        public function get first():Object { return _list[0]; }
        /** Returns the last operator in the list. */
        public function get last():Object { return _list[_list.length-1]; }
        
        // --------------------------------------------------------------------
        
        /**
         * Creates a new OperatorList.
         * @param ops an ordered set of operators to include in the list.
         */
        public function OperatorList(...ops) {
            for each (var op:IOperator in ops) {
                add(op);
            }
        }

        /** @inheritDoc */
        public function setup():void
        {
            for each (var op:IOperator in _list) {
                op.setup();
            }
        }
        
        /**
         * Proxy method for retrieving operators from the internal array.
         */
        flash_proxy override function getProperty(name:*):*
        {
            return _list[name];
        }

        /**
         * Proxy method for setting operators in the internal array.
         */
        flash_proxy override function setProperty(name:*, value:*):void
        {
            if (value is IOperator) {
                var op:IOperator = IOperator(value);
                _list[name] = op;
                op.visualization = this.visualization;
            } else {
                throw new ArgumentError("Input value must be an IOperator.");
            }
        }
        
        /**
         * Returns the operator at the specified position in the list
         * @param i the index into the operator list
         * @return the requested operator
         */
        public function getOperatorAt(i:uint):IOperator
        {
            return _list[i];
        }
        
        /**
         * Removes the operator at the specified position in the list
         * @param i the index into the operator list
         * @return the removed operator
         */
        public function removeOperatorAt(i:uint):IOperator
        {
            return Arrays.removeAt(_list, i) as IOperator;
        }
        
        /**
         * Set the operator at the specified position in the list
         * @param i the index into the operator list
         * @param op the operator to place in the list
         * @return the operator previously at the index
         */
        public function setOperatorAt(i:uint, op:IOperator):IOperator
        {
            var old:IOperator = _list[i];
            op.visualization = visualization;
            _list[i] = op;
            return old;
        }
        
        /**
         * Adds an operator to the end of this list.
         * @param op the operator to add
         */
        public function add(op:IOperator):void
        {
            op.visualization = visualization;
            _list.push(op);
        }
        
        /**
         * Removes an operator from this list.
         * @param op the operator to remove
         * @return true if the operator was found and removed, false otherwise
         */
        public function remove(op:IOperator):Boolean
        {
            return Arrays.remove(_list, op) >= 0;
        }
        
        /**
         * Removes all operators from this list.
         */
        public function clear():void
        {
            Arrays.clear(_list);
        }
        
        /** @inheritDoc */
        public function operate(t:Transitioner=null):void
        {
            t = (t!=null ? t : Transitioner.DEFAULT);
            for each (var op:IOperator in _list) {
                if (op.enabled) op.operate(t);
            }
        }
        
        // -- MXML ------------------------------------------------------------
        
        /** @private */
        public function initialized(document:Object, id:String):void
        {
            // do nothing
        }
        
    } // end of class OperatorList
}