package usda.weru.weps;

import de.schlichtherle.truezip.file.TFile;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.beans.*;
import javax.help.*;
import java.net.URL;
import java.util.*;

import java.util.jar.*;
import java.util.zip.*;
import java.net.URLClassLoader;
import usda.weru.util.*;

/**
 * Displays and edits barrier data
 * This class builds a GUI that allows the user to mention width, height and porosity
 * of the barrier for the field whose area in acres is computed in the acres field
 * of the constructed GUI.
 */
public class BarDialog extends usda.weru.weps.gui.BarDialog_n implements PropertyChangeListener {

    private static final long serialVersionUID = 1L;

    private PropertyChangeSupport changes = null;
    private Barrier bi;
    private final String barrierName;
    private HelpSet hs;
    /**
     * Help broker object for the context sensitive and generic application level help from the UI screens.
     */
    public HelpBroker hb;
    private final ConvertedValue cWidth = new ConvertedValue(Util.SIUnits, "meter");
    private final ConvertedValue cHeight = new ConvertedValue(Util.SIUnits, "meter");
    private final ConvertedValue cLength = new ConvertedValue(Util.SIUnits, "meter");
    private final ConvertedValue cPorosity = new ConvertedValue();
    private final ConvertedValue cArea = new ConvertedValue(Util.SIUnits, "m^2");

    /**
     ** Constructs barrier edit dialog box
     ** @param parentFrame          Used to connect to the weps frame
     ** @param title		title to diplay
     ** @param mU			measurement units to use for display (SI or US)
     ** @param bi			data to display
     ** @param changes		property change support to dispatch changes
     ** @param barrierName          Specifies which barrier to edit.
     ** @param length		Tells the length of that barrier
     */
    public BarDialog(Frame parentFrame, String title, String mU, Barrier bi, PropertyChangeSupport changes, String barrierName, String length) {
        //super(parentFrame, true);
        super(parentFrame);
        cWidth.addDisplayUnits(Util.USUnits, "foot");
        cHeight.addDisplayUnits(Util.USUnits, "foot");
        cArea.addDisplayUnits(Util.USUnits, "acre");
        cArea.addDisplayUnits(Util.SIUnits, "hectare");
        setUnits(mU);
        setTitle(title);
        this.bi = bi;
        this.barrierName = barrierName;
        this.changes = changes;
        JL_title.setText(bi.name);
        try {
            cLength.setValue(Double.parseDouble(length));
            cWidth.setValue(Double.parseDouble(bi.width));
            cHeight.setValue(Double.parseDouble(bi.height));
            cPorosity.setValue(Double.parseDouble(bi.porosity));
        } catch (NumberFormatException e) {
            //System.err.println("bad number passed to barrier width, height or porosity " + e);
        }
        //System.out.println("BD_BD: " + bi.width + " " + length + " " + Util.convertMetersToFeet(bi.width, measurementUnits, 2)
        //				   + " " + Util.convertMetersToFeet(length, measurementUnits,2));
        //}}
        addHelp();
        updateDisplayValues();

        updateReadonlyState();
    }

    private void updateDisplayValues() {
        JTF_Width.setValueInternal(cWidth.getDisplayValue());
        JTF_Height.setValueInternal(cHeight.getDisplayValue());
        JTF_Porosity.setValueInternal(cPorosity.getDisplayValue());
        cArea.setValue(cLength.getValue() * cWidth.getValue());
        JTF_Area.setValueInternal(cArea.getDisplayValue());
    }

    private void updateReadonlyState() {
        String readonlyString = ConfigData.getDefault().getData(ConfigData.BarriersReadonly);
        boolean readonly = "1".equals(readonlyString);

        JTF_Height.setEditable(!readonly);
        JTF_Width.setEditable(!readonly);
        JTF_Porosity.setEditable(!readonly);
    }

    /**
     * Sets this dialog's state to visible or hidden depending upon the flag 
     * value passed as an argument.
     * @param b Flag that makes the dialog visible or hidden.
     */
    @Override
    public void setVisible(boolean b) {
        if (b) {
            Rectangle bounds = (getParent()).getBounds();
            Dimension size = getSize();
            setLocation(bounds.x + (bounds.width - size.width) / 2,
                    bounds.y + (bounds.height - size.height) / 2);
        }

        super.setVisible(b);
    }

    /**
     * Cancels the changes made on the dialog and closes it.
     * @param evt The button click/enter event that triggers this method to 
     * perform its operations.
     */
    @Override
    public void JB_cancel_actionPerformed(java.awt.event.ActionEvent evt) {
        this.dispose();
    }

    /**
     * Accepts the changes made to the data on the dialog and closes it.
     * @param event
     */
    @Override
    public void JB_OK_actionPerformed(java.awt.event.ActionEvent event) {
        try {
            boolean changeFlg = false;
            if (!bi.width.equals(cWidth.toStringValue())) {
                changeFlg = true;
            }
            if (!bi.height.equals(cHeight.toStringValue())) {
                changeFlg = true;
            }
            if (!bi.porosity.equals(cPorosity.toStringValue())) {
                changeFlg = true;
            }
            if (changeFlg) {
                if (!bi.name.startsWith("<mod>")) {
                    String oldName = bi.name;
                    bi = new Barrier();
                    bi.name = "<mod> " + oldName;
                }
                bi.width = cWidth.toStringValue();
                bi.height = cHeight.toStringValue();
                bi.porosity = cPorosity.toStringValue();
                changes.firePropertyChange(barrierName, null, bi.makeBarrier());
            }
            this.setVisible(false);
        } catch (Exception e) {
        }

    }

    /**
     * fields to display
     */
    /**
     * Sets the labels for the barrier dialog frame.
     * @param arg The string that acts as a heading or label for the dialog.
     */
    public void setBarrierLabel(String arg) {
        JL_title.setText(arg);
    }

    /**
     * Handlers for user input fields: Width 
     * @param event Handler that responds to the focus lost on width text 
     * field saving or accepting all the data that is modified or entered.
     */
    @Override
    public void JTFWidth_focusLost(java.awt.event.FocusEvent event) {
        cWidth.setDisplayValue(JTF_Width.getValue());
        updateDisplayValues();
    }

    /**
     * Handlers for user input fields: Porosity
     * @param event Handler that responds to the focus lost on porosity text 
     * field saving or accepting all the data that is modified or entered.
     */
    @Override
    public void JTFPorosity_focusLost(java.awt.event.FocusEvent event) {
        cPorosity.setDisplayValue(JTF_Porosity.getValue());
        updateDisplayValues();
    }

    /**
     * Handlers for user input fields: Height
     * @param event Handler that responds to the focus lost on height text 
     * field saving or accepting all the data that is modified or entered.
     */
    @Override
    public void JTFHeight_focusLost(java.awt.event.FocusEvent event) {
        cHeight.setDisplayValue(JTF_Height.getValue());
        updateDisplayValues();
    }

    /**
     * Handlers for user input fields: Width 
     * @param event Handler that responds to the mouse/keyboard enter event for
     * width text field saving or accepting all the data that is modified or 
     * entered.
     */
    @Override
    public void JTFWidth_actionPerformed(java.awt.event.ActionEvent event) {
        cWidth.setDisplayValue(JTF_Width.getValue());
        updateDisplayValues();
    }

    /**
     * Handlers for user input fields: Porosity 
     * @param event Handler that responds to the mouse/keyboard enter event for
     * porosity text field saving or accepting all the data that is modified or 
     * entered.
     */
    @Override
    public void JTFPorosity_actionPerformed(java.awt.event.ActionEvent event) {
        cPorosity.setDisplayValue(JTF_Porosity.getValue());
        updateDisplayValues();
    }

    /**
     * Handlers for user input fields: Height 
     * @param event Handler that responds to the mouse/keyboard enter event for
     * height text field saving or accepting all the data that is modified or 
     * entered.
     */
    @Override
    public void JTFHeight_actionPerformed(java.awt.event.ActionEvent event) {
        cHeight.setDisplayValue(JTF_Height.getValue());
        updateDisplayValues();
    }
    private String measurementUnits = Util.SIUnits;

    void setUnits(String measurementUnits) {
        this.measurementUnits = measurementUnits;
        cWidth.setDisplaySystem(measurementUnits);
        cHeight.setDisplaySystem(measurementUnits);
        cArea.setDisplaySystem(measurementUnits);

        if (measurementUnits.equals(Util.USUnits)) {
            JL_ft1.setText("ft");
            JL_ft2.setText("ft");
            JL_ac.setText("ac");
        } else {
            JL_ft1.setText("m");
            JL_ft2.setText("m");
            JL_ac.setText("ha");
        }
    }

    /**
     *  This method recognises the change in status or property of the components in various containers
     *  that are registered to be tracked for any changes with configuration panel and eventually react 
     *  to those events.
     * @param evt This parameter that decides where the event was triggered and from which component so 
     * that changes could be made to update other registered screens.
     */
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
    }

    /**
     * Links the "Help" jar file and the "HelpSet" files from the jar and help
     * directories so that they could be used in the application for seeting up 
     * the generic as well as context sensitive help system for GUI's visible.
     */
    private void addHelp() {

        String jarFilePath = "jar/WepsHelp.jar";
        String dirFilePath = "help/MasterWeps.hs";
        TFile file_jar = new TFile(jarFilePath);
        TFile file_dir = new TFile(dirFilePath);
        if (file_jar.exists()) {
            //System.out.println("Barrier Panel:addHelp-jar file found");
            jarHelp(jarFilePath);
        } else if (file_dir.exists()) {
            //System.out.println("Barrier Panel:addhelp-dir help file found");
            dirHelp(dirFilePath);
        } else {
            //System.out.println("Barrier Panel:addHelp-There are no help files");
            return;
        }

    } //end of addhelp()

    /**
     * Invokes the directory "Help" for barrier panel.
     * @param dirFilePath The directory path where the help file is located.
     */
    private void dirHelp(String dirFilePath) {

        //System.out.println("Barrier Panel:dirHelp-we are executing from here");
//        try {
        URL hsURL;
        try {
            hsURL = new URL((new TFile(".")).toURI().toURL(), dirFilePath);
        } catch (MalformedURLException ex) {
            Logger.getLogger(BarDialog.class.getName()).log(Level.SEVERE, null, ex);
            return;
        }
        try {
            hs = new HelpSet(null, hsURL);
            addCSH(hs);
        } catch (HelpSetException ex) {
            Logger.getLogger(BarDialog.class.getName()).log(Level.SEVERE, null, ex);
            return;
        }
        //System.out.println("Barrier Panel - Found help set at " + hsURL);
//        } catch (Exception ee) {
//            //System.err.println("Barrier Panel - HelpSet not found");
//            return;
//        }
//        //new CSH.DisplayHelpFromSource(getHelpBroker()));

    } //end of dirHelp

    /**
     * Adds the context sensitive help to all the components in the container
     * for the barrier panel GUI.
     * @param dirFilePath The directory path where the help file is located.
     */
    private void addCSH(HelpSet hs) {
        hb = hs.createHelpBroker();
        hb.enableHelpKey(getRootPane(), "barEditPanel_html", hs);
        ActionListener helper = new CSH.DisplayHelpFromSource(hb);
        JB_help.addActionListener(helper);

    }//end of addCSH

    /**
     * Adds the helpset from the jar help file by accessing it from its jar 
     * help file location. 
     * @param jarFilePath The directory path where the jar help file is located.
     */
    private void jarHelp(String jarFilePath) {

        //System.out.println("Barrier Panel:jarHelp-we are executing from here"); 
        hs = getHelpSet(jarFilePath);
        if (hs != null) {
            addCSH(hs);
        } else {
            //System.out.println("Barrier Panel - Can't find the helpSet-file.");
            return;
        }

    } //end of jarHelp

    /**
     * Returns the helpset file which is included in the jar file
     * @param jarFilePath The directory path where the jar help file is located.
     * @return Returns the help set object that will be referenced in future for retrival of various
     * descriptive terms regarding the context sensitive and generic application level help.
     */
    protected HelpSet getHelpSet(String jarFilePath) {
        HelpSet hs = null;
        String hsName = null;
        JarFile jar;
        try {
            jar = new JarFile(jarFilePath);
        } catch (IOException ex) {
            Logger.getLogger(BarDialog.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
        Enumeration<JarEntry> entries = jar.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) entries.nextElement();
            String entryName = entry.getName();
            if (entryName.endsWith(".hs")) {
                hsName = entryName;
                break;
            }
        }
        URL url[] = getURLs("file:" + jarFilePath);
        ClassLoader cl = new URLClassLoader(url);
        URL hsUrl = HelpSet.findHelpSet(cl, hsName);
        try {
            hs = new HelpSet(cl, hsUrl);
        } catch (HelpSetException ex) {
            Logger.getLogger(BarDialog.class.getName()).log(Level.SEVERE, null, ex);
        }
        return hs;
    }

    /**
     * This function generates a URL from a string and then puts it into a 
     * URL array.
     * @param spec The specification string that indicates the URLs.
     */
    private URL[] getURLs(String spec) {
        Vector<URL> v = new Vector<>();
        try {
            URL url = new URL(spec);
            v.addElement(url);
        } catch (Exception ex) {
            //System.err.println("Barrier Panel - cannot create URL for "+spec);
        }
        URL back[] = new URL[v.size()];
        v.copyInto(back);
        return back;
    }
}
