    package flare.vis.controls
    {
        import flare.util.Arrays;
        import flare.vis.Visualization;

        import flash.utils.flash_proxy;
        import flash.utils.Proxy;

        import mx.core.IMXMLObject;

        /**
         * A ControlList maintains a sequential chain of controls for interacting
         * with a visualization. Controls may perform operations such as selection,
         * panning, zooming, and expand/contract. Controls can be added to a
         * ControlList using the <code>add</code> method. Once added, controls can be
         * retrieved and set using their index in the lists, either with array
         * notation (<code>[]</code>) or with the <code>getControlAt</code> and
         * <code>setControlAt</code> methods.
         */
        public class ControlList extends Proxy implements IMXMLObject
        {
            protected var _vis:Visualization;
            protected var _list:/*IControl*/Array = [];

            /** The visualization manipulated by these controls. */
            public function get visualization():Visualization { return _vis; }
            public function set visualization(v:Visualization):void {
                _vis = v;
                for each (var ic:IControl in _list) { ic.attach(v);    }
            }

            /** An array of the controls contained in the control list. */
            public function set list(ctrls:Array):void {
                // first remove all current operators
                while (_list.length > 0) {
                    removeControlAt(_list.length-1);
                }
                // then add the new operators
                for each (var ic:IControl in ctrls) {
                    add(ic);
                }
            }

            /** The number of controls in the list. */
            public function get length():uint { return _list.length; }

            // --------------------------------------------------------------------

            /**
             * Creates a new ControlList.
             * @param ops an ordered set of controls to include in the list.
             */
            public function ControlList(...controls) {
                for each (var ic:IControl in controls) {
                    add(ic);
                }
            }

            /**
             * Proxy method for retrieving controls from the internal array.
             */
            flash_proxy override function getProperty(name:*):*
            {
                return _list[name];
            }

            /**
             * Proxy method for setting controls in the internal array.
             */
            flash_proxy override function setProperty(name:*, value:*):void
            {
                if (value is IControl) {
                    var ic:IControl = IControl(value);
                    _list[name].detach();
                    _list[name] = ic;
                    ic.attach(_vis);
                } else {
                    throw new ArgumentError("Input value must be an IControl.");
                }
            }

            /**
             * Returns the control at the specified position in the list
             * @param i the index into the control list
             * @return the requested control
             */
            public function getControlAt(i:uint):IControl
            {
                return _list[i];
            }

            /**
             * Removes the control at the specified position in the list
             * @param i the index into the control list
             * @return the removed control
             */
            public function removeControlAt(i:uint):IControl
            {
                var ic:IControl = Arrays.removeAt(_list, i) as IControl;
                if (ic) ic.detach();
                return ic;
            }

            /**
             * Set the control at the specified position in the list
             * @param i the index into the control list
             * @param ic the control to place in the list
             * @return the control previously at the index
             */
            public function setControlAt(i:uint, ic:IControl):IControl
            {
                var old:IControl = _list[i];
                _list[i] = ic;
                old.detach();
                ic.attach(_vis);
                return old;
            }

            /**
             * Adds a control to the end of this list.
             * @param ic the control to add
             */
            public function add(ic:IControl):void
            {
                ic.attach(_vis);
                _list.push(ic);
            }

            /**
             * Adds a control at the specified index in the list.
             * @param ic the control to add
             * @param idx the index into the list
             */
            public function addAt(ic:IControl, idx:int):void
            {
                ic.attach(_vis);
                _list.splice(idx, 0, ic);
            }

            /**
             * Removes an control from this list.
             * @param ic the control to remove
             * @return true if the control was found and removed, false otherwise
             */
            public function remove(ic:IControl):IControl
            {
                var idx:int = Arrays.remove(_list, ic);
                if (idx >= 0) ic.detach();
                return ic;
            }

            /**
             * Removes all controls from this list.
             */
            public function clear():void
            {
                for each (var ic:IControl in _list) { ic.detach(); }
                Arrays.clear(_list);
            }

            // -- MXML ------------------------------------------------------------

            /** @private */
            public function initialized(document:Object, id:String):void
            {
                // do nothing
            }

        } // end of class ControlList
    }