/*
 A basic implementation of the JFrame class.
 */
package usda.weru.weps;

import java.beans.*;
import javax.help.*;
import org.apache.log4j.Logger;
import usda.weru.util.*;

/**
 * This panel takes the simulation region information to know the area like its length, beadth
 * and its orientation.
 */
public class RegPanel extends usda.weru.weps.gui.RegPanel_n implements PropertyChangeListener {

    private static final long serialVersionUID = 1L;

    private static final Logger logger = Logger.getLogger(RegPanel.class);

    /**
     *
     */
    public static final String SHAPE_RECTANGLE = "rectangle";

    /**
     *
     */
    public static final String SHAPE_SQUARE = "square";

    /**
     *
     */
    public static final String SHAPE_CIRCLE = "circle";

    /**
     *
     */
    public static final String SHAPE_HALF_CIRCLE_VE = "half_circle_ve";

    /**
     *
     */
    public static final String SHAPE_HALF_CIRCLE_VW = "half_circle_vw";

    /**
     *
     */
    public static final String SHAPE_HALF_CIRCLE_HN = "half_circle_hn";

    /**
     *
     */
    public static final String SHAPE_HALF_CIRCLE_HS = "half_circle_hs";

    /**
     *
     */
    public static final String SHAPE_QUARTER_CIRCLE_NE = "quarter_circle_ne";

    /**
     *
     */
    public static final String SHAPE_QUARTER_CIRCLE_SE = "quarter_circle_se";

    /**
     *
     */
    public static final String SHAPE_QUARTER_CIRCLE_SW = "quarter_circle_sw";

    /**
     *
     */
    public static final String SHAPE_QUARTER_CIRCLE_NW = "quarter_circle_nw";
    private static final String[] shapesComboBox = {"Rectangle", "Square", "Circle", "Half Circle VE", "Half Circle VW", "Half Circle HS", "Half Circle HN", "Quarter Circle NE", "Quarter Circle SE", "Quarter Circle SW", "Quarter Circle NW"};
    private static final String[] shapesRunFile = {SHAPE_RECTANGLE, SHAPE_SQUARE, SHAPE_CIRCLE, SHAPE_HALF_CIRCLE_VE, SHAPE_HALF_CIRCLE_VW, SHAPE_HALF_CIRCLE_HS, SHAPE_HALF_CIRCLE_HN, SHAPE_QUARTER_CIRCLE_NE, SHAPE_QUARTER_CIRCLE_SE, SHAPE_QUARTER_CIRCLE_SW, SHAPE_QUARTER_CIRCLE_NW};

    String measurementUnits = Util.SIUnits;
    private String c_shape = SHAPE_RECTANGLE;
    private String c_fromShape = null;
    private double Angle = 0;
    private final ConvertedValue cXLen = new ConvertedValue(Util.SIUnits, "meter");
    private final ConvertedValue cYLen = new ConvertedValue(Util.SIUnits, "meter");
    private final ConvertedValue cRadius = new ConvertedValue(Util.SIUnits, "meter");
    private final ConvertedValue cArea = new ConvertedValue(Util.SIUnits, "m^2");
    double to = 1;
    double from = 1;
    double lengthRatio = 1; // = x / y

    /**
     * Default constructor that makes the main panel holding other components visible after 
     * initializing and calling the parent/super class which in this case is a GUI RegPanel and Panel.
     */
    public RegPanel() {
        super();
        JP_main.setVisible(true);
        for (String shapesComboBox1 : shapesComboBox) {
            JCB_Shape.addItem(makeStringObject(shapesComboBox1));
        }

        cXLen.setValue(0);
        cYLen.setValue(0);
        cRadius.setValue(0);
        cArea.setValue(0);

        //Add units for US measurement system
        cXLen.addDisplayUnits(Util.USUnits, "foot");
        cYLen.addDisplayUnits(Util.USUnits, "foot");
        cRadius.addDisplayUnits(Util.USUnits, "foot");
        cArea.addDisplayUnits(Util.USUnits, "acre");
        cArea.addDisplayUnits(Util.SIUnits, "hectare");

        displayFields();
        addHelp();
    }

    private Object makeStringObject(final String item) {
        return new Object() {
            @Override
            public String toString() {
                return item;
            }
        };
    }

    private static int convertComboShapeToIndex(String shapeFromCombo) {
        for (int i = 0; i < shapesComboBox.length; i++) {
            if (shapeFromCombo.equals(shapesComboBox[i])) {
                return i;
            }
        }
        return 0;
    }

    static int convertRunFileShapeToIndex(String shapeFromCombo) {
        for (int i = 0; i < shapesRunFile.length; i++) {
            if (shapeFromCombo.equals(shapesRunFile[i])) {
                return i;
            }
        }
        return 0;
    }

    private boolean isShape(String test) {
        return isShape(test, c_shape);
    }

    /**
     *
     * @param test
     * @param against
     * @return
     */
    public static boolean isShape(String test, String against) {
        if (test == null || against == null) {
            return false;
        }
        return against.equals(test);
    }

    /**
     * Single argument constructor that makes the main panel holding other components visible after 
     * initializing and calling the parent/super class which in this case is a GUI RegPanel and Panel.
     * @param sTitle The string title that will show up on the GUI when executed as its name/title.
     */
    public RegPanel(String sTitle) {
        this();
        setTitle(sTitle);
    }

    private void addHelp() {
        CSH.setHelpIDString(JP_main, "regPanel_html");
        //CSH.setHelpIDString(JL_Title, "WEPS simregion");
        CSH.setHelpIDString(JL_shape, "regShape_html");
        CSH.setHelpIDString(JCB_Shape, "regShape_html");
        CSH.setHelpIDString(JL_xlength, "regDim_html");
        CSH.setHelpIDString(JTF_XLength, "regDim_html");
        CSH.setHelpIDString(JL_ylength, "regDim_html");
        CSH.setHelpIDString(JTF_YLength, "regDim_html");
        CSH.setHelpIDString(JL_area, "regDim_html");
        CSH.setHelpIDString(JTF_Area, "regDim_html");
        CSH.setHelpIDString(JL_orientation, "regOrientation_html");
        CSH.setHelpIDString(JTF_Orientation, "regOrientation_html");
    }

    /**
     * Entry point for testing standalone. Used if this dialog is rum as an independent application. 
     * If executed, makes the GUI for Simulation Region panel visible.
     * @param args These are the command line arguments passed to the main method. 
     */
    static public void main(String args[]) {
        RegPanel rp = new RegPanel("RegPanel Test");
        rp.setVisible(true);
        RunFileData rfd = new RunFileData();
        ConfigData cd = ConfigData.getDefault();
        cd.addPropertyChangeListener(rp);
        rp.addPropertyChangeListener(cd);
        rfd.addPropertyChangeListener(rp);
        rp.addPropertyChangeListener(rfd);
    }

    @Override
    public void JTF_XLength_focusLost(java.awt.event.FocusEvent event) {
        JTFXLength_actionPerformed(null);
    }

    @Override
    public void JTF_YLength_focusLost(java.awt.event.FocusEvent event) {
        JTFYLength_actionPerformed(null);
    }

    @Override
    public void JTF_Orientation_focusLost(java.awt.event.FocusEvent event) {
        JTFOrientation_actionPerformed(null);
    }

    @Override
    public void JTFXLength_actionPerformed(java.awt.event.ActionEvent event) {
        cXLen.setDisplayValue(JTF_XLength.getValue());
        calculateShapes(true);
        changes.firePropertyChange(RunFileData.XLength, null, cXLen.toStringValue());
    }

    @Override
    public void JTFYLength_actionPerformed(java.awt.event.ActionEvent event) {
        cYLen.setDisplayValue(JTF_YLength.getValue());
        calculateShapes(true);
        changes.firePropertyChange(RunFileData.YLength, null, cYLen.toStringValue());
    }

    @Override
    public void JTFOrientation_actionPerformed(java.awt.event.ActionEvent event) {
        Angle = JTF_Orientation.getValue();
        if (Angle > 45) {
            Angle = 45;
            JTF_Orientation.setValueInternal(Angle);
        }
        if (Angle < -45) {
            Angle = -45;
            JTF_Orientation.setValueInternal(Angle);
        }

        changes.firePropertyChange(RunFileData.RegionAngle, null, Double.toString(Angle));

    }

    /**
     *
     * @param event
     */
    @Override
    public void JTF_Radius_actionPerformed(java.awt.event.ActionEvent event) {
        cRadius.setDisplayValue(JTF_Radius.getValue());
        calculateShapes(true);
        changes.firePropertyChange(RunFileData.Radius, null, cRadius.toStringValue());
    }

    /**
     *
     * @param event
     */
    @Override
    public void JTF_Radius_focusLost(java.awt.event.FocusEvent event) {
        if (!event.isTemporary()) {
            JTF_Radius_actionPerformed(null);
        }
    }

    /**
     *
     * @param event
     */
    @Override
    public void JTF_Area_actionPerformed(java.awt.event.ActionEvent event) {
        //User has entered area.  Calculate the X / Y / Radius;
        cArea.setDisplayValue(JTF_Area.getValue());
        double areaSquareMeters = cArea.getValue();  //Converts hectares to square meters
        if (isShape(SHAPE_RECTANGLE)) {
            cYLen.setValue(Math.sqrt(areaSquareMeters / lengthRatio));
            cXLen.setValue(cYLen.getValue() * lengthRatio);
            changes.firePropertyChange(RunFileData.XLength, null, cXLen.toStringValue());
            changes.firePropertyChange(RunFileData.YLength, null, cYLen.toStringValue());
        } else if (isShape(SHAPE_SQUARE)) {
            cXLen.setValue(Math.sqrt(areaSquareMeters));
            cYLen.setValue(cXLen.getValue());
            changes.firePropertyChange(RunFileData.XLength, null, cXLen.toStringValue());
            changes.firePropertyChange(RunFileData.YLength, null, cYLen.toStringValue());
        } else if (isShape(SHAPE_CIRCLE)) {
            cRadius.setValue(Math.sqrt(areaSquareMeters / Math.PI));
            changes.firePropertyChange(RunFileData.Radius, null, cRadius.toStringValue());
        } else if (isShape(SHAPE_HALF_CIRCLE_HS) || isShape(SHAPE_HALF_CIRCLE_HN) || isShape(SHAPE_HALF_CIRCLE_VE) || isShape(SHAPE_HALF_CIRCLE_VW)) {
            cRadius.setValue(Math.sqrt(areaSquareMeters * 2 / Math.PI));
            changes.firePropertyChange(RunFileData.Radius, null, cRadius.toStringValue());
        } else if (isShape(SHAPE_QUARTER_CIRCLE_NE) || isShape(SHAPE_QUARTER_CIRCLE_SE) || isShape(SHAPE_QUARTER_CIRCLE_SW) || isShape(SHAPE_QUARTER_CIRCLE_NW)) {
            cRadius.setValue(Math.sqrt(areaSquareMeters * 4 / Math.PI));
            changes.firePropertyChange(RunFileData.Radius, null, cRadius.toStringValue());
        }
        calculateShapes(true);
    }

    /**
     *
     * @param event
     */
    @Override
    public void JTF_Area_focusLost(java.awt.event.FocusEvent event) {
        if (!event.isTemporary()) {
            JTF_Area_actionPerformed(null);
        }
    }

    /**
     * Recognizes and takes appropriate actions on registered properties from different screens to 
     * update and synchronize data and GUI screens as needed
     * @param e The event itself that is responsible for triggering the change required for registered 
     * properties/components.
     */
    @Override
    public void propertyChange(PropertyChangeEvent e) {
//		//System.out.println("pC: " + e.getPropertyName());
//		if (e.getPropertyName().equals(RunFileData.SimPoint1)) {
        if (e.getPropertyName().equals(RunFileData.XLength)) {
            try {
                cXLen.setValue(Double.parseDouble((String) e.getNewValue()));
            } catch (NumberFormatException k) {
                //System.err.println("RP-pC: bad XLen from rfd " + k);
            }
            calculateShapes();
//		} else if (e.getPropertyName().equals(RunFileData.SimPoint2)) {
        } else if (e.getPropertyName().equals(RunFileData.YLength)) {
            try {
                cYLen.setValue(Double.parseDouble((String) e.getNewValue()));
            } catch (NumberFormatException k) {
                //System.err.println("RP-pC: bad YLen from rfd " + k);
            }
            calculateShapes();
        } else if (e.getPropertyName().equals(RunFileData.RegionAngle)) {
            try {
                Angle = Double.parseDouble((String) e.getNewValue());
                if (Angle > 45) {
                    Angle = 45;
                }
                if (Angle < -45) {
                    Angle = -45;
                }
            } catch (NumberFormatException k) {
                //System.err.println("RP-pC: bad Angle from rfd " + k);
            }
            JTF_Orientation.setValueInternal(Angle);
        } else if (e.getPropertyName().equals(RunFileData.Radius)) {
            try {
                cRadius.setValue(Double.parseDouble((String) e.getNewValue()));
            } catch (NumberFormatException k) {
                //System.err.println("RP-pC: bad Radius from rfd " + k);
            }
            calculateShapes();

        } else if (e.getPropertyName().equals(RunFileData.Shape)) {
            c_shape = e.getNewValue().toString();
            if (isShape(SHAPE_RECTANGLE) || isShape(SHAPE_SQUARE) || isShape(SHAPE_CIRCLE)) {
                to = 1;
            } else if (isShape(SHAPE_HALF_CIRCLE_VE) || isShape(SHAPE_HALF_CIRCLE_VW) || isShape(SHAPE_HALF_CIRCLE_HS) || isShape(SHAPE_HALF_CIRCLE_HN)) {
                to = .5;
            } else if (isShape(SHAPE_QUARTER_CIRCLE_NE) || isShape(SHAPE_QUARTER_CIRCLE_SE) || isShape(SHAPE_QUARTER_CIRCLE_SW) || isShape(SHAPE_QUARTER_CIRCLE_NW)) {
                to = .25;
            }
            JCB_Shape.setSelectedIndex(convertRunFileShapeToIndex(c_shape));
            displayFields();
            calculateShapes(true);
            from = to;
            c_fromShape = c_shape;

        } else if (e.getPropertyName().equals(ConfigData.Units)) {
            measurementUnits = e.getNewValue().toString();
            cXLen.setDisplaySystem(measurementUnits);
            cYLen.setDisplaySystem(measurementUnits);
            cRadius.setDisplaySystem(measurementUnits);
            cArea.setDisplaySystem(measurementUnits);
            if (measurementUnits.equals(Util.USUnits)) {
                JL_xlengthUnits.setText("ft");
                JL_ylengthUnits.setText("ft");
                JL_areaUnits.setText("ac");
                JL_radiusUnits.setText("ft");
            } else {
                JL_xlengthUnits.setText("m");
                JL_ylengthUnits.setText("m");
                JL_areaUnits.setText("ha");
                JL_radiusUnits.setText("m");
            }
            updateDisplayValues();

        }
    }

    private void updateDisplayValues() {
        JTF_XLength.setValueInternal(cXLen.getDisplayValue());
        JTF_YLength.setValueInternal(cYLen.getDisplayValue());
        JTF_Radius.setValueInternal(cRadius.getDisplayValue());
        JTF_Area.setValueInternal(cArea.getDisplayValue());
    }
    private final PropertyChangeSupport changes = new PropertyChangeSupport(this);

    /**
     * Allows the container to add or register some other components to recognize the changes that occur 
     * on this component
     * @param l The listener that listens and reacts towards the the changes to be reflected.
     */
    @Override
    public void addPropertyChangeListener(PropertyChangeListener l) {
        changes.addPropertyChangeListener(l);
    }

    /**
     * Allows the container to remove or de-register some other components and stop recognizing the 
     * changes that occur on this component
     * @param l The listener that does not listen and react towards the the changes to be reflected.
     */
    @Override
    public void removePropertyChangeListener(PropertyChangeListener l) {
        changes.removePropertyChangeListener(l);
    }

    /**
     *
     * @param evt
     */
    @Override
    public void JCB_Shape_itemStateChanged(java.awt.event.ItemEvent evt) {
        String selectedShape = JCB_Shape.getSelectedItem().toString();
        int shapeIndex = convertComboShapeToIndex(selectedShape);
        c_shape = shapesRunFile[shapeIndex];
        changes.firePropertyChange(RunFileData.Shape, null, c_shape);
    }

    private void displayFields() {
        //Hide everything but the combo box.
        //Fields
        JTF_XLength.setVisible(false);
        JTF_YLength.setVisible(false);
        JTF_YLength.setEditable(true);
        JTF_Radius.setVisible(false);
        //Labels
        JL_area.setVisible(false);
        JL_xlength.setVisible(false);
        JL_ylength.setVisible(false);
        JL_orientation.setVisible(false);
        JL_radius.setVisible(false);
        //Units
        JL_areaUnits.setVisible(false);
        JL_xlengthUnits.setVisible(false);
        JL_ylengthUnits.setVisible(false);
        JL_orientationUnits.setVisible(false);
        JL_radiusUnits.setVisible(false);

        boolean circles = false;
        boolean rectangles = false;

        if (isShape(SHAPE_CIRCLE) || isShape(SHAPE_HALF_CIRCLE_VE) || isShape(SHAPE_HALF_CIRCLE_VW) || isShape(SHAPE_HALF_CIRCLE_HN) || isShape(SHAPE_HALF_CIRCLE_HS) || isShape(SHAPE_QUARTER_CIRCLE_NE) || isShape(SHAPE_QUARTER_CIRCLE_SE) || isShape(SHAPE_QUARTER_CIRCLE_SW) || isShape(SHAPE_QUARTER_CIRCLE_NW)) {
            circles = true;
        } else if (isShape(SHAPE_SQUARE)) {
            rectangles = true;
            JTF_YLength.setEditable(false);
        } else {
            rectangles = true;
        }

        if (rectangles) {
            //Fields
            JTF_XLength.setVisible(true);
            JTF_YLength.setVisible(true);
            //Labels
            JL_area.setVisible(true);
            JL_xlength.setVisible(true);
            JL_ylength.setVisible(true);
            JL_orientation.setVisible(true);
            //Units
            JL_areaUnits.setVisible(true);
            JL_xlengthUnits.setVisible(true);
            JL_ylengthUnits.setVisible(true);
            JL_orientationUnits.setVisible(true);
        } else if (circles) {
            //Fields
            JTF_Radius.setVisible(true);
            //Labels
            JL_area.setVisible(true);
            JL_orientation.setVisible(true);
            JL_radius.setVisible(true);
            //Units
            JL_areaUnits.setVisible(true);
            JL_orientationUnits.setVisible(true);
            JL_radiusUnits.setVisible(true);
        }

    }

    private void calculateShapes() {
        calculateShapes(false);
    }

    private void calculateShapes(boolean save) {
        double ratio = to / from;
        boolean changedXLen = false;
        boolean changedYLen = false;
        boolean changedRadius = false;
        //Calculate rectangle from shape
        if (isShape(SHAPE_RECTANGLE)) {
            if (ratio > 1 || isShape(SHAPE_SQUARE, c_fromShape) || isShape(SHAPE_CIRCLE, c_fromShape)) {
                double area = cXLen.getValue() * cYLen.getValue();
                if (lengthRatio == 0 || Double.isNaN(lengthRatio) || Double.isInfinite(lengthRatio)) {
                    lengthRatio = 1;
                }
                cYLen.setValue(Math.sqrt(area / lengthRatio));
                cXLen.setValue(cYLen.getValue() * lengthRatio);
                changedXLen = true;
                changedYLen = true;
                from = to;
            }
            lengthRatio = cXLen.getValue() / cYLen.getValue();
            if (lengthRatio == 0 || Double.isNaN(lengthRatio) || Double.isInfinite(lengthRatio)) {
                lengthRatio = 1;
            }
            cRadius.setValue(Math.sqrt((cXLen.getValue() * cYLen.getValue()) / Math.PI));
            changedRadius = true;
        } else if (isShape(SHAPE_SQUARE)) {
            if (ratio > 1 || isShape(SHAPE_RECTANGLE, c_fromShape)) {
                cXLen.setValue(Math.sqrt(cXLen.getValue() * cYLen.getValue()));
                changedXLen = true;
                from = to;
            }
            cYLen.setValue(cXLen.getValue());
            cRadius.setValue(Math.sqrt((cXLen.getValue() * cXLen.getValue()) / Math.PI));
            changedYLen = true;
            changedRadius = true;
        } else if (isShape(SHAPE_CIRCLE)) {
            cRadius.setValue(Math.sqrt(cArea.getValue() / Math.PI));
            cXLen.setValue(Math.sqrt(Math.PI * cRadius.getValue() * cRadius.getValue()));
            cYLen.setValue(cXLen.getValue());
            changedXLen = true;
            changedYLen = true;
        } else if (isShape(SHAPE_HALF_CIRCLE_HS) || isShape(SHAPE_HALF_CIRCLE_HN)) {
            cRadius.setValue(Math.sqrt(cArea.getValue() * 2 / Math.PI));
            cXLen.setValue(Math.sqrt(Math.PI * cRadius.getValue() * cRadius.getValue()));
            cYLen.setValue(cXLen.getValue() / 2);
            changedXLen = true;
            changedYLen = true;
        } else if (isShape(SHAPE_HALF_CIRCLE_VE) || isShape(SHAPE_HALF_CIRCLE_VW)) {
            cRadius.setValue(Math.sqrt(cArea.getValue() * 2 / Math.PI));
            cYLen.setValue(Math.sqrt(Math.PI * cRadius.getValue() * cRadius.getValue()));
            cXLen.setValue(cYLen.getValue() / 2);
            changedXLen = true;
            changedYLen = true;
        } else if (isShape(SHAPE_QUARTER_CIRCLE_NE) || isShape(SHAPE_QUARTER_CIRCLE_SE) || isShape(SHAPE_QUARTER_CIRCLE_SW) || isShape(SHAPE_QUARTER_CIRCLE_NW)) {
            cRadius.setValue(Math.sqrt(cArea.getValue() * 4 / Math.PI));
            cXLen.setValue(Math.sqrt(Math.PI * cRadius.getValue() * cRadius.getValue() / 4));
            cYLen.setValue(cXLen.getValue());
            changedXLen = true;
            changedYLen = true;
        }

        //Calculate area, square meters to hectares
        cArea.setValue(cXLen.getValue() * cYLen.getValue());

        updateDisplayValues();
        if (save) {
            if (changedXLen) {
                changes.firePropertyChange(RunFileData.XLength, null, cXLen.toStringValue());
            }
            if (changedYLen) {
                changes.firePropertyChange(RunFileData.YLength, null, cYLen.toStringValue());
            }
            if (changedRadius) {
                changes.firePropertyChange(RunFileData.Radius, null, cRadius.toStringValue());
            }
        }
    }
}
