package simulate;

import simulate.units.*;
import javax.swing.*;

/**
 * A simple display of a single value in a textbox with an associated label
 */
 
public class DisplayBox extends Display {
    
    protected JLabel label;
    protected JTextField value;
    protected Meter meter;
    int precision;
    private boolean useCurrentValue;  //need to set up flags for recent, average, current
    protected Unit unit;
    
    public DisplayBox() {
        label = new JLabel("Label");
        value = new JTextField("");
        value.setEditable(false);
        setPrecision(3);
        add(label);
        add(value);
        unit = Unit.Null.UNIT;
        setUseCurrentValue(true);
    }
    
    public void setUnit(Unit u) {
        unit = u;
        setLabel();
    }
    public Unit getUnit() {return unit;}
    
    public int getPrecision() {return precision;}
    public void setPrecision(int n) {
        value.setColumns(n);
        precision = n;
    }
    
    public void setMeter(Meter m) {
        meter = m;
        setLabel();
    }
    
    private void setLabel() {
        String suffix = (unit.symbol().length() > 0) ? " ("+unit.symbol()+")" : "";
        if(meter != null) label.setText(meter.getLabel()+suffix);
    }
    
    public void setLabel(String s) {label.setText(s);}
    public String getLabel() {return label.getText();}
    
    public void doUpdate() {
        if(useCurrentValue) {
            value.setText(format(unit.fromSim(meter.mostRecent()),precision));
        }
        else {
            value.setText(format(unit.fromSim(meter.average()),precision));
        }
    }
    
    /**
     * Sets flag indicating if plot should be of instantaneous (current) value or running average
     */
    public void setUseCurrentValue(boolean b) {
        useCurrentValue = b;
        if(meter == null) return;
        if(!useCurrentValue && !meter.isActive()) {System.out.println("Warning: setting to use averages but meter is not active");}
    }
    public boolean getUseCurrentValue() {return useCurrentValue;}

    
    // Some convenience subclasses of DisplayBox
    public static class Energy extends DisplayBox {
    
        public Energy(Phase phase) {
            super();
            this.setMeter(phase.energy);
            this.setUnit(Simulation.unitSystem().energy());
        }
    }
    
    /**
     * Formats a double with a specified number of digits.
     * When java converts a <tt>double</tt> to a <tt>String</tt>
     * it retains the full precision of the number. This can
     * generate 15 decimal places! This method truncates this output
     * to some specified number of decimal places.
     * @param d the double to format
     * @param precision the number of digits desired
     * @return returns the formatted string
     *
     * Taken from the comphys package of Richard Gonsalves of the
     * SUNY Buffalo Department of Physics
     */

    public static String format (double d, int precision) {

        if (d == Double.NaN ||
            d == Double.POSITIVE_INFINITY ||
            d == Double.NEGATIVE_INFINITY)
            return Double.toString(d);
        
        StringBuffer buffer = new StringBuffer(20);
        
        if (d < 0) {
            d = -d;
            buffer.append('-');
        }

        if (d == 0) {
            buffer.append("0.0");
            for (int p = 0; p < precision - 1; p++)
                buffer.append('0');
            return buffer.toString();
        }

        int exponent = 0;
        while (d >= 10) {
            ++exponent;
            d /= 10;
        }
        while (d < 1) {
            --exponent;
            d *= 10;
        }

        if (precision < 0)
            precision = -precision;
        int p = precision;
        while (--p > 0)
            d *= 10;
        long ld = (long) Math.round(d);
        char[] digits = new char[precision];
        p = precision;
	long ld_div_10 = 0;
	long ld_save = ld;
        while (--p >= 0) {
	    ld_div_10 = ld / 10;
            digits[p] = (char) ('0' + ( ld - (ld_div_10 * 10) ));
            ld = ld_div_10;
        }
	if (ld_div_10 > 0) {
	    ld = ld_save / 10;
	    p = precision;
	    while (--p >= 0) {
		ld_div_10 = ld / 10;
		digits[p] = (char) ('0' + ( ld - (ld_div_10 * 10) ));
		ld = ld_div_10;
	    }
	    ++exponent;
	}

        int decimalPoint = 0;
        if (Math.abs(exponent) < 6 || Math.abs(exponent) < precision) {
            while (exponent > 0) {
                ++decimalPoint;
                --exponent;
            }
            while (exponent < 0) {
                --decimalPoint;
                ++exponent;
            }
        }

        if (decimalPoint < 0) {
            buffer.append("0.");
            while (decimalPoint < -1) {
                buffer.append("0");
                ++decimalPoint;
            }
        }

        for (p = 0; p < precision; p++) {
            buffer.append(digits[p]);
            if (p == decimalPoint)
                if (p < precision - 1)
                    buffer.append(".");
        }

        if (exponent != 0)
            buffer.append("E" + exponent);

        return buffer.toString();

    }

}