package cnp.ew.charts;

import cnp.ew.util.*;

public class CpValueAxisModel extends CpDefaultObservable
{
    public static final int MIN_CHANGED=0;
    public static final int MAX_CHANGED=1;
    public static final int MAJOR_STEP_CHANGED=2;

    double min=0.0;
    double max=100.0;
    double majorStep=10.0;

    public String toString()
    {
        return "AxisModel(min: " + min + " max:" + max + " step: " + majorStep + ")";
    }

    public double getMin()
    {
        return min;
    }

    public void setMin(double newMin)
    {
        min = newMin;
        notifyObservers(MIN_CHANGED, new Double(min));
    }

    public double getMax()
    {
        return max;
    }

    public void setMax(double newMax)
    {
        max = newMax;
        notifyObservers(MAX_CHANGED, new Double(max));
    }

    public double getMajorStep()
    {
        return majorStep;
    }

    public void setMajorStep(double newStep)
    {
        majorStep = newStep;
        notifyObservers(MAJOR_STEP_CHANGED, new Double(max));
    }

    public double getPercentageForValue(double value)
    {
        return value / (max - min);
    }

    public int getMajorStepCount()
    {
        return (int)((max - min) / majorStep);
    }

    /**
     * Given a maximum value and a real maximum number of steps,
     * this routine will calculate the 'best' combination of a
     * new maximum and a step value, so that they appear well
     * on a tick mark line.  The possible steps are run in multiples
     * of 2, 5, 10, and 20, finding the one that has largest number
     * of ticks, yet with the smallest difference between the real
     * maximum and the suggested maximum (which is a multiple of
     * the step).
     *
     * When it's finished, the max and majorStepValue variables will
     * be updated to the new value.
     */
    public void autoRecalcMaxAndStep(double realMax, int maxSteps)
    {
        double decimalPart=realMax;
        int exponent = 0;
        // First, boil it down to a number between 1 and 10.
        while (decimalPart >= 10) {
            decimalPart /= 10;
            exponent++;
        }
        while (decimalPart < 1) {
            decimalPart *= 10;
            exponent--;
        }


        // Then step through the acceptable step possibilities,
        // looking for the one with the closest maximum above
        // the real maximum, that will fit into the
        // maximum number of steps.
        double bestStepValue = 10.0;
        double smallestStepRemainder = decimalPart;
        double curStepValue, curStepRemainder;

        double [] steps = { 2.0, 1.0, 0.5, 0.2, 0.1 };
        for (int i = 0; i < steps.length; i++) {
            curStepValue = steps[i];
            if (decimalPart / curStepValue <= maxSteps) {
                curStepRemainder = decimalPart - (((int)(decimalPart / curStepValue)) * curStepValue);
                if (curStepRemainder <= smallestStepRemainder) {

                    smallestStepRemainder = curStepRemainder;
                    bestStepValue = curStepValue;
                }
            }
        }


        bestStepValue *= Math.pow(10, exponent);

        double bestMax = 0.0;
        while (bestMax < realMax) {
            bestMax += bestStepValue;
        }
        double [] result = new double[2];
        setMax(bestMax);
        setMajorStep(bestStepValue);
    }
}

