package flare.scale
{
    import flare.util.Maths;
    import flare.util.Strings;
    
    /**
     * Base class for representing quantitative numerical data scales.
     */
    public class QuantitativeScale extends Scale
    {
        /** The minimum data value. */
        protected var _dmin:Number;
        /** The maximum data value. */
        protected var _dmax:Number;
        /** The minimum value of the scale range. */
        protected var _smin:Number;
        /** The maximum value of the scale range. */
        protected var _smax:Number;
        /** The number base of the scale. */
        protected var _base:Number;

        /**
         * Creates a new QuantitativeScale.
         * @param min the minimum data value
         * @param max the maximum data value
         * @param base the number base to use
         * @param flush the flush flag for scale padding
         * @param labelFormat the formatting pattern for value labels
         */
        public function QuantitativeScale(min:Number=0, max:Number=0, base:Number=10,
            flush:Boolean=false, labelFormat:String=Strings.DEFAULT_NUMBER)
        {
            this.base = base;
            this.dataMin = min;
            this.dataMax = max;
            this.flush = flush;
            this.labelFormat = labelFormat;
        }

        /** @inheritDoc */
        public override function clone() : Scale
        {
            throw new Error("This is an abstract class");
        }

        // -- Properties ------------------------------------------------------
        
        /** @inheritDoc */
        public override function set flush(val:Boolean):void
        {
            _flush = val; updateScale();
        }
        
        /** @inheritDoc */
        public override function get min():Object { return dataMin; }
        public override function set min(o:Object):void { dataMin = Number(o); }
        
        /** @inheritDoc */
        public override function get max():Object { return dataMax; }
        public override function set max(o:Object):void { dataMax = Number(o); }
        
        /** The minimum data value. This property is the same as the
         *  <code>minimum</code> property, but properly typed. */
        public function get dataMin():Number
        {
            return _dmin;
        }
        public function set dataMin(val:Number):void
        {
            _dmin = val; updateScale();
        }

        /** The maximum data value. This property is the same as the
         *  <code>maximum</code> property, but properly typed. */
        public function get dataMax():Number
        {
            return _dmax;
        }
        public function set dataMax(val:Number):void
        {
            _dmax = val; updateScale();
        }
        
        /** The minimum value of the scale range. */
        public function get scaleMin():Number
        {
            return _smin;
        }
        
        /** The maximum value of the scale range. */
        public function get scaleMax():Number
        {
            return _smax;
        }
        
        /** The number base used by the scale.
         *  By default, base 10 numbers are assumed. */
        public function get base():Number
        {
            return _base;
        }
        public function set base(val:Number):void
        {
            _base = val;
        }
        
        // -- Scale Methods ---------------------------------------------------

        /**
         * Updates the scale range after a change to the data range.
         */
        protected function updateScale():void
        {
            if (!_flush) {
                var step:Number = getStep(_dmin, _dmax);
                _smin = Math.floor(_dmin / step) * step;
                _smax = Math.ceil(_dmax / step) * step;
            } else {
                _smin = _dmin;
                _smax = _dmax;
            }
        }

        /**
         * Returns the default step value between label values. The step is
         * computed according to the current number base.
         * @param min the minimum scale value
         * @param max the maximum scale value
         * @return the default step value between label values
         */
        protected function getStep(min:Number, max:Number):Number
        {
            var range:Number = max - min;
            var exp:Number = Math.round(Maths.log(range, _base)) - 1;
            return Math.pow(base, exp);
        }
        
        /** @inheritDoc */
        public override function lookup(f:Number):Object
        {
            return null;
        }
        
        /** @inheritDoc */
        public override function interpolate(value:Object):Number
        {
            return interp(Number(value));
        }

        /**
         * Returns the interpolation fraction for the given input number.
         * @param val the input number
         * @return the interpolation fraction for the input value
         */
        protected function interp(val:Number):Number
        {
            return -1;
        }
        
        /** @inheritDoc */
        public override function values(num:int=-1):/*Number*/Array
        {
            var a:Array = new Array();
            var range:Number = _smax - _smin;

            if (range == 0) {
                a.push(_smin);
            } else {
                var step:Number = getStep(_smin, _smax);
                var stride:Number = num<0 ? 1 : Math.max(1, Math.floor(range/(step*num)));
                for (var x:Number = _smin; x <= _smax; x += stride*step) {
                    a.push(x);
                }
            }
            return a;
        }

    } // end of class QuantitativeScale
}