/**
 * jqPlot
 * Pure JavaScript plotting plugin using jQuery
 *
 * Version: @VERSION
 * Revision: @REVISION
 *
 * Copyright (c) 2009-2013 Chris Leonello
 * jqPlot is currently available for use in all personal or commercial projects 
 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
 * choose the license that best suits your project and use it accordingly. 
 *
 * Although not required, the author would appreciate an email letting him 
 * know of any substantial use of jqPlot.  You can reach the author at: 
 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
 *
 * If you are feeling kind and generous, consider supporting the project by
 * making a donation at: http://www.jqplot.com/donate.php .
 *
 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
 *
 *     version 2007.04.27
 *     author Ash Searle
 *     http://hexmen.com/blog/2007/03/printf-sprintf/
 *     http://hexmen.com/js/sprintf.js
 *     The author (Ash Searle) has placed this code in the public domain:
 *     "This code is unrestricted: you are free to use it however you like."
 * 
 */
(function($) {
    /**
    * The following code was generaously given to me a while back by Scott Prahl.
    * He did a good job at computing axes min, max and number of ticks for the 
    * case where the user has not set any scale related parameters (tickInterval,
    * numberTicks, min or max).  I had ignored this use case for a long time,
    * focusing on the more difficult case where user has set some option controlling
    * tick generation.  Anyway, about time I got this into jqPlot.
    * Thanks Scott!!
    */
    
    /**
    * Copyright (c) 2010 Scott Prahl
    * The next three routines are currently available for use in all personal 
    * or commercial projects under both the MIT and GPL version 2.0 licenses. 
    * This means that you can choose the license that best suits your project 
    * and use it accordingly. 
    */

    // A good format string depends on the interval. If the interval is greater 
    // than 1 then there is no need to show any decimal digits. If it is < 1.0, then
    // use the magnitude of the interval to determine the number of digits to show.
    function bestFormatString (interval)
    {
        var fstr;
        interval = Math.abs(interval);
        if (interval >= 10) {
            fstr = '%d';
        }

        else if (interval > 1) {
            if (interval === parseInt(interval, 10)) {
                fstr = '%d';
            }
            else {
                fstr = '%.1f';
            }
        }

        else {
            var expv = -Math.floor(Math.log(interval)/Math.LN10);
            fstr = '%.' + expv + 'f';
        }
        
        return fstr; 
    }

    var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5];

    var _getLowerFactor = function(f) {
        var i = _factors.indexOf(f);
        if (i > 0) {
            return _factors[i-1];
        }
        else {
            return _factors[_factors.length - 1] / 100;
        }
    };

    var _getHigherFactor = function(f) {
        var i = _factors.indexOf(f);
        if (i < _factors.length-1) {
            return _factors[i+1];
        }
        else {
            return _factors[0] * 100;
        }
    };

    // Given a fixed minimum and maximum and a target number ot ticks
    // figure out the best interval and 
    // return min, max, number ticks, format string and tick interval
    function bestConstrainedInterval(min, max, nttarget) {
        // run through possible number to ticks and see which interval is best
        var low = Math.floor(nttarget/2);
        var hi = Math.ceil(nttarget*1.5);
        var badness = Number.MAX_VALUE;
        var r = (max - min);
        var temp;
        var sd;
        var bestNT;
        var gsf = $.jqplot.getSignificantFigures;
        var fsd;
        var fs;
        var currentNT;
        var bestPrec;

        for (var i=0, l=hi-low+1; i<l; i++) {
            currentNT = low + i;
            temp = r/(currentNT-1);
            sd = gsf(temp);

            temp = Math.abs(nttarget - currentNT) + sd.digitsRight;
            if (temp < badness) {
                badness = temp;
                bestNT = currentNT;
                bestPrec = sd.digitsRight;
            }
            else if (temp === badness) {
                // let nicer ticks trump number ot ticks
                if (sd.digitsRight < bestPrec) {
                    bestNT = currentNT;
                    bestPrec = sd.digitsRight;
                }
            }

        }

        fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight));
        if (fsd === 0) {
            fs = '%d';
        }
        else {
            fs = '%.' + fsd + 'f';
        }
        temp = r / (bestNT - 1);
        // min, max, number ticks, format string, tick interval
        return [min, max, bestNT, fs, temp];
    }

    // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
    // it is based soley on the range and number of ticks.  So if user specifies
    // number of ticks, use this.
    function bestInterval(range, numberTicks) {
        numberTicks = numberTicks || 7;
        var minimum = range / (numberTicks - 1);
        var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10));
        var residual = minimum / magnitude;
        var interval;
        // "nicest" ranges are 1, 2, 5 or powers of these.
        // for magnitudes below 1, only allow these. 
        if (magnitude < 1) {
            if (residual > 5) {
                interval = 10 * magnitude;
            }
            else if (residual > 2) {
                interval = 5 * magnitude;
            }
            else if (residual > 1) {
                interval = 2 * magnitude;
            }
            else {
                interval = magnitude;
            }
        }
        // for large ranges (whole integers), allow intervals like 3, 4 or powers of these.
        // this helps a lot with poor choices for number of ticks. 
        else {
            if (residual > 5) {
                interval = 10 * magnitude;
            }
            else if (residual > 4) {
                interval = 5 * magnitude;
            }
            else if (residual > 3) {
                interval = 4 * magnitude;
            }
            else if (residual > 2) {
                interval = 3 * magnitude;
            }
            else if (residual > 1) {
                interval = 2 * magnitude;
            }
            else {
                interval = magnitude;
            }
        }

        return interval;
    }

    // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
    // it is based soley on the range of data, number of ticks must be computed later.
    function bestLinearInterval(range, scalefact) {
        scalefact = scalefact || 1;
        var expv = Math.floor(Math.log(range)/Math.LN10);
        var magnitude = Math.pow(10, expv);
        // 0 < f < 10
        var f = range / magnitude;
        var fact;
        // for large plots, scalefact will decrease f and increase number of ticks.
        // for small plots, scalefact will increase f and decrease number of ticks.
        f = f/scalefact;

        // for large plots, smaller interval, more ticks.
        if (f<=0.38) {
            fact = 0.1;
        }
        else if (f<=1.6) {
            fact = 0.2;
        }
        else if (f<=4.0) {
            fact = 0.5;
        }
        else if (f<=8.0) {
            fact = 1.0;
        }
        // for very small plots, larger interval, less ticks in number ticks
        else if (f<=16.0) {
            fact = 2;
        }
        else {
            fact = 5;
        } 

        return fact*magnitude; 
    }

    function bestLinearComponents(range, scalefact) {
        var expv = Math.floor(Math.log(range)/Math.LN10);
        var magnitude = Math.pow(10, expv);
        // 0 < f < 10
        var f = range / magnitude;
        var interval;
        var fact;
        // for large plots, scalefact will decrease f and increase number of ticks.
        // for small plots, scalefact will increase f and decrease number of ticks.
        f = f/scalefact;

        // for large plots, smaller interval, more ticks.
        if (f<=0.38) {
            fact = 0.1;
        }
        else if (f<=1.6) {
            fact = 0.2;
        }
        else if (f<=4.0) {
            fact = 0.5;
        }
        else if (f<=8.0) {
            fact = 1.0;
        }
        // for very small plots, larger interval, less ticks in number ticks
        else if (f<=16.0) {
            fact = 2;
        }
        // else if (f<=20.0) {
        //     fact = 3;
        // }
        // else if (f<=24.0) {
        //     fact = 4;
        // }
        else {
            fact = 5;
        } 

        interval = fact * magnitude;

        return [interval, fact, magnitude];
    }

    // Given the min and max for a dataset, return suitable endpoints
    // for the graphing, a good number for the number of ticks, and a
    // format string so that extraneous digits are not displayed.
    // returned is an array containing [min, max, nTicks, format]
    $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) {
        // Set to preserve EITHER min OR max.
        // If min is preserved, max must be free.
        keepMin = (keepMin === null) ? false : keepMin;
        keepMax = (keepMax === null || keepMin) ? false : keepMax;
        // if endpoints are equal try to include zero otherwise include one
        if (axis_min === axis_max) {
            axis_max = (axis_max) ? 0 : 1;
        }

        scalefact = scalefact || 1.0;

        // make sure range is positive
        if (axis_max < axis_min) {
            var a = axis_max;
            axis_max = axis_min;
            axis_min = a;
        }

        var r = [];
        var ss = bestLinearInterval(axis_max - axis_min, scalefact);

        var gsf = $.jqplot.getSignificantFigures;
        
        if (numberTicks == null) {

            // Figure out the axis min, max and number of ticks
            // the min and max will be some multiple of the tick interval,
            // 1*10^n, 2*10^n or 5*10^n.  This gaurantees that, if the
            // axis min is negative, 0 will be a tick.
            if (!keepMin && !keepMax) {
                r[0] = Math.floor(axis_min / ss) * ss;  // min
                r[1] = Math.ceil(axis_max / ss) * ss;   // max
                r[2] = Math.round((r[1]-r[0])/ss+1.0);  // number of ticks
                r[3] = bestFormatString(ss);            // format string
                r[4] = ss;                              // tick Interval
            }

            else if (keepMin) {
                r[0] = axis_min;                                        // min
                r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0);     // number of ticks
                r[1] = axis_min + (r[2] - 1) * ss;                      // max
                var digitsMin = gsf(axis_min).digitsRight;
                var digitsSS = gsf(ss).digitsRight;
                if (digitsMin < digitsSS) {
                    r[3] = bestFormatString(ss);                        // format string
                }
                else {
                    r[3] = '%.' + digitsMin + 'f';
                }
                r[4] = ss;                                              // tick Interval
            }

            else if (keepMax) {
                r[1] = axis_max;                                        // max
                r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0);     // number of ticks
                r[0] = axis_max - (r[2] - 1) * ss;                      // min
                var digitsMax = gsf(axis_max).digitsRight;
                var digitsSS = gsf(ss).digitsRight;
                if (digitsMax < digitsSS) {
                    r[3] = bestFormatString(ss);                        // format string
                }
                else {
                    r[3] = '%.' + digitsMax + 'f';
                }
                r[4] = ss;                                              // tick Interval
            }
        }

        else {
            var tempr = [];

            // Figure out the axis min, max and number of ticks
            // the min and max will be some multiple of the tick interval,
            // 1*10^n, 2*10^n or 5*10^n.  This gaurantees that, if the
            // axis min is negative, 0 will be a tick.
            tempr[0] = Math.floor(axis_min / ss) * ss;  // min
            tempr[1] = Math.ceil(axis_max / ss) * ss;   // max
            tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0);    // number of ticks
            tempr[3] = bestFormatString(ss);            // format string
            tempr[4] = ss;                              // tick Interval

            // first, see if we happen to get the right number of ticks
            if (tempr[2] === numberTicks) {
                r = tempr;
            }

            else {

                var newti = bestInterval(tempr[1] - tempr[0], numberTicks);

                r[0] = tempr[0];                        // min
                r[2] = numberTicks;                     // number of ticks
                r[4] = newti;                           // tick interval
                r[3] = bestFormatString(newti);         // format string
                r[1] = r[0] + (r[2] - 1) * r[4];        // max
            }
        }

        return r;
    };

    $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval;
    $.jqplot.LinearTickGenerator.bestInterval = bestInterval;
    $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents;
    $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval;

})(jQuery);