/* Copyright (c) 2005 Stanford University and Christopher Bruns
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including 
 * without limitation the rights to use, copy, modify, merge, publish, 
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * Created on May 24, 2006
 * Original author: Christopher Bruns
 */
package org.simtk.isimsu;

import java.util.*;
import javax.swing.*;

class LogarithmicJSlider 
extends JSlider
// implements ChangeListener
// implements ComponentListener
{
    // Hard code actual range
    protected final int internalMinimum = 1;
    protected final int internalMaximum = 2000;
    protected double userMinimum = 1;
    protected double userMaximum = 1000;
    private double userValue = 100;
    protected int userMajorTickSpacing = 0;
    protected int userMinorTickSpacing = 0;
    protected Set<Double> majorTickPositions;
    protected Set<Double> minorTickPositions;

    // Constructors
    public LogarithmicJSlider(int orientation) {
        super(orientation);
        initialize(1, 100);
    }

    public LogarithmicJSlider(int min, int max) {
        initialize(min, max);
    }

    public LogarithmicJSlider(int min, int max, int value) {
        initialize(min, max);
        setValue(value);
    }

    public LogarithmicJSlider(int orientation, int min, int max, int value) {
        super(orientation);
        initialize(min, max);
        setValue(value);
    }

    public LogarithmicJSlider() {
        initialize(1, 100);
    }


    
    
//    public int getMaximum() {
//        return (int) userMaximum;
//    }
    public double getDoubleMaximum() {
        return userMaximum;
    }

//    public int getMinimum() {
//        return (int) userMinimum;
//    }
    public double getDoubleMinimum() {
        return userMinimum;
    }

    public void setDoubleRange(double min, double max) {
        if ( (getDoubleMinimum() == min) && (userMaximum == max)) return;
        
        userMinimum = min;
        userMaximum = max;

        int internalValue = internalValueForUserValue(getUserValue());
        super.setValue(internalValue);
        // setUserValue(userValue); // no op?

        updateTickSpacing();            
    }
    
    // Return the internal value, not the user value, because the base
    // class maybe makes some assumptions about how this function works.
//    public int getValue() {
//        return super.getValue();
//    }

    public double getUserValue() {
        return this.userValue;
    }
    
//    public void setValue(int userValue) {
//        int internalValue = internalValueForUserValue(userValue);
//        super.setValue(internalValue);
//    }
    public void setUserValue(double userValue) {
        this.userValue = userValue;
        
        // If the user is dragging the slider, don't fight by
        // setting the position.
        if (! getValueIsAdjusting()) { // user is NOT dragging slider
            int internalValue = internalValueForUserValue(userValue);
            super.setValue(internalValue);
        }
    }
    
    public void setMinorTickSpacing(int tickSpacing) {
        userMinorTickSpacing = tickSpacing;
        
        if (tickSpacing <= 1) super.setMinorTickSpacing(0);
        else {
            int first = internalValueForUserValue(getDoubleMinimum());
            int second = internalValueForUserValue(getDoubleMinimum() * tickSpacing);
            int spacing = second - first;
            super.setMinorTickSpacing(spacing);
        }
    }
    public void setMajorTickSpacing(int tickSpacing) {
        userMajorTickSpacing = tickSpacing;
        
        if (tickSpacing <= 1) super.setMajorTickSpacing(0);
        else {
            int first = internalValueForUserValue(getDoubleMinimum());
            int second = internalValueForUserValue(getDoubleMinimum() * tickSpacing);
            int spacing = second - first;
            super.setMajorTickSpacing(spacing);
        }
    }
    
    public void setLabelTable(Dictionary labels) {
        Hashtable<Integer, Object> internalLabelTable = new Hashtable<Integer, Object>();

        Enumeration numbers = labels.keys();
        while (numbers.hasMoreElements()) {
            Object number = numbers.nextElement();
            Object label = labels.get(number);
            if (number instanceof Number) {
                double userValue = ((Number) number).doubleValue();
                int internalValue = internalValueForUserValue(userValue);
                internalLabelTable.put(new Integer(internalValue), label);
            }
        }
        super.setLabelTable(internalLabelTable);
    }

//    public void stateChanged(ChangeEvent event) {            
//        if (event.getSource() == this) {
//            double v = userValueForInternalValue(super.getValue());
//            if (this.userValue != v)
//                this.userValue = v;
//        }
//    }
    

    protected void updateTickSpacing() {
        setMajorTickSpacing(userMajorTickSpacing);
        setMinorTickSpacing(userMinorTickSpacing);
    }
    
    protected void initialize(double min, double max) {
        // addChangeListener(this);
        super.setMinimum(internalMinimum);
        super.setMaximum(internalMaximum);
        userMinimum = min;
        userMaximum = max;
    }
    
    protected double userValueForInternalValue(int sliderValue) {
        double sliderFraction = internalValueFraction(sliderValue);
        
        // user values (i.e. log scale)
        double logMin = Math.log(getDoubleMinimum());
        double logMax = Math.log(getDoubleMaximum());
        double logUserValue = logMin + sliderFraction * (logMax - logMin);
        double userValue = Math.pow(Math.E, logUserValue);
        
        userValue = Math.min(getDoubleMaximum(), userValue);
        userValue = Math.max(getDoubleMinimum(), userValue);
        
        return userValue;
    }
    
    protected int internalValueForUserValue(double userValue) {
        double sliderFraction = userValueFraction(userValue);
        
        int internalValue = 
            (int) (super.getMinimum() + 
                    sliderFraction * (super.getMaximum() - super.getMinimum()));
        
        internalValue = Math.max(super.getMinimum(), internalValue);
        internalValue = Math.min(super.getMaximum(), internalValue);
        
        return internalValue;
    }
    
    protected double internalValueFraction(int internalValue) {
        // internal values
        double min = super.getMinimum();
        double max = super.getMaximum();
        double sliderFraction = (internalValue - min) / (max - min);
        
        return sliderFraction;
    }
    
    protected double userValueFraction(double userValue) {
        double logUserValue = Math.log(userValue);
        double logMin = Math.log(getDoubleMinimum());
        double logMax = Math.log(getDoubleMaximum());
        double sliderFraction = (logUserValue - logMin) / (logMax - logMin);

        return sliderFraction;
    }
}

