/*
 * StationChooser.java
 *
 * Created on Jul 31, 2009, 3:06:27 PM
 */
package usda.weru.weps.location.chooser;

import java.awt.BorderLayout;
import java.awt.Component;
import java.util.HashMap;
import org.jscience.geography.coordinates.LatLong;

import java.util.Map;
import javax.measure.Measurable;
import javax.measure.quantity.Length;
import javax.measure.unit.Unit;
import javax.swing.JLabel;
import javax.swing.JPanel;
import usda.weru.util.WepsFileChooser;
import usda.weru.weps.RunFileBean;
import usda.weru.weps.location.Station;
import usda.weru.weps.location.StationMode;

/**
 *
 * @author Joseph Levin <joelevin@weru.ksu.edu>
 */
public class StationChooser extends JPanel {

    private static final long serialVersionUID = 1L;

    /**
     *
     */
    public static final String PROP_DISTANCE_UNITS = "distanceUnits";

    /**
     *
     */
    public static final String PROP_DISTANCE = "distance";

    /**
     *
     */
    public static final String PROP_VIEW = "view";

    /**
     *
     */
    public static final String PROP_MORE_BUTTON_VISIBLE = "moreButtonVisible";

    /**
     *
     */
    public static final String PROP_FILE_TYPE = "fileType";

    /**
     *
     */
    public static final String PROP_STATION_CHOICES = "stationChoices";

    /**
     *
     */
    public static final String PROP_SELECTED_STATION = "selectedStation";

    /**
     *
     */
    public static final String PROP_LATLONG = "latLong";

    /**
     *
     */
    public static final String PROP_TEXT = "text";

    /**
     *
     */
    public static final String PROP_STATION_MODE = "stationMode";

    /**
     *
     */
    public static final String PROP_ALLOWED_STATION_MODES = "allowedStationModes";

    /**
     *
     */
    public static final String PROP_RUN_FILE_BEAN = "runFileBean";

    /**
     *
     */
    public static final String PROP_STATION_TYPE = "stationType";

    /**
     *
     */
    public static final String PROP_LABEL = "label";

    /**
     *
     */
    public static final String PROP_SUB_LABEL = "subLabel";

    private View c_view;
    private Measurable<Length> c_distance;
    private Unit<Length> c_distanceUnits;
    private WepsFileChooser.Filetype c_fileType = WepsFileChooser.Filetype.FILE;
    private Station[] c_stationChoices;
    private String c_text;
    private StationMode c_stationMode;
    private StationMode[] c_allowedStationModes;
    private Station c_selectedStation;
    private LatLong c_latlong;
    private RunFileBean c_rfb;
    private RunFileBean.StationType c_stationType;
    private JLabel c_label;
    private JLabel c_subLabel;

    /** Creates new form StationChooser */
    public StationChooser() {
        initComponents();
        setCircleActive(false);
        setMoreButtonVisible(false);

        //default view is label
        setView(View.Label);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        moreButton = new javax.swing.JButton();
        circle = new com.blogspot.gyfus.LoadingCircle();
        viewPanel = new javax.swing.JPanel();

        moreButton.setText("...");
        moreButton.setToolTipText("Change station selection mode.");
        moreButton.setFocusable(false);
        moreButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                moreButtonActionPerformed(evt);
            }
        });

        circle.setRotationSpeed(75);
        circle.setStylePreset(com.blogspot.gyfus.LoadingCircle.StylePresets.FireFox);

        javax.swing.GroupLayout circleLayout = new javax.swing.GroupLayout(circle);
        circle.setLayout(circleLayout);
        circleLayout.setHorizontalGroup(
            circleLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 22, Short.MAX_VALUE)
        );
        circleLayout.setVerticalGroup(
            circleLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 20, Short.MAX_VALUE)
        );

        viewPanel.setLayout(new java.awt.BorderLayout());

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(circle, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(viewPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 164, Short.MAX_VALUE)
                .addGap(0, 0, 0)
                .addComponent(moreButton, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(viewPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 20, Short.MAX_VALUE)
            .addComponent(moreButton, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addComponent(circle, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        );
    }// </editor-fold>//GEN-END:initComponents

    /**
     * Opens the StationModeChooser dialog to allow the user to select the mode.
     * @param evt
     */
    private void moreButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moreButtonActionPerformed
        StationModeChooser dialog = new StationModeChooser(getAllowedStationModes(), getStationMode());

        if (dialog.showDialog(this) == StationModeChooser.APPROVE_OPTION) {
            StationMode mode = dialog.getSelectedStationMode();
            setStationMode(mode);
        }

    }//GEN-LAST:event_moreButtonActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private com.blogspot.gyfus.LoadingCircle circle;
    private javax.swing.JButton moreButton;
    private javax.swing.JPanel viewPanel;
    // End of variables declaration//GEN-END:variables

    /**
     *
     * @param rfb
     */
    public void setRunFileBean(RunFileBean rfb) {
        RunFileBean old = c_rfb;
        c_rfb = rfb;
        firePropertyChange(PROP_RUN_FILE_BEAN, old, c_rfb);
    }

    /**
     *
     * @return
     */
    public RunFileBean getRunFileBean() {
        return c_rfb;
    }

    /**
     *
     * @param type
     */
    public void setStationType(RunFileBean.StationType type) {
        RunFileBean.StationType old = c_stationType;
        c_stationType = type;
        firePropertyChange(PROP_STATION_TYPE, old, c_stationType);
    }

    /**
     *
     * @return
     */
    public RunFileBean.StationType getStationType() {
        return c_stationType;
    }

    /**
     *
     * @param latlong
     */
    public void setLatLong(LatLong latlong) {
        LatLong old = c_latlong;
        c_latlong = latlong;
        firePropertyChange(PROP_LATLONG, old, c_latlong);
    }

    /**
     *
     * @return
     */
    public LatLong getLatLong() {
        return c_latlong;
    }

    /**
     *
     * @param modes
     */
    public void setAllowedStationModes(StationMode... modes) {
        StationMode[] old = c_allowedStationModes;
        c_allowedStationModes = modes;
        firePropertyChange(PROP_ALLOWED_STATION_MODES, old, c_allowedStationModes);

        //update the more button's visibility
        setMoreButtonVisible(modes != null && modes.length > 1);

    }

    /**
     *
     * @return
     */
    public StationMode[] getAllowedStationModes() {
        return c_allowedStationModes != null ? c_allowedStationModes : new StationMode[0];
    }

    /**
     *
     * @param mode
     */
    public void setStationMode(StationMode mode) {

        StationMode old = c_stationMode;
        if (old != null) {
            //turn off the old mode, most won't do anything but perhaps clean up.
            old.uninstallView(this);
        }

        if (isAllowed(mode)) {
            if (mode != null) {
                //turn on the new mode for this chooser
                setEnabled(true);
                mode.installView(this);
            }
        } else {
            //mode is null, we need to disable any station selections
            setEnabled(false);
            setView(View.Text);
            setText(mode != null ? "" : "Configuration Error");
        }

        c_stationMode = mode;
        firePropertyChange(PROP_STATION_MODE, old, c_stationMode);
    }

    private boolean isAllowed(StationMode mode) {
        for (StationMode temp : getAllowedStationModes()) {
            if (temp == mode) {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @return
     */
    public StationMode getStationMode() {
        return c_stationMode;
    }

    /**
     *
     * @param visible
     */
    public void setMoreButtonVisible(boolean visible) {
        boolean old = moreButton.isVisible();
        moreButton.setVisible(visible);
        firePropertyChange(PROP_MORE_BUTTON_VISIBLE, old, visible);
    }

    /**
     *
     * @return
     */
    public boolean isMoreButtonVisible() {
        return moreButton.isVisible();
    }

    /**
     * Some views display the distance of the station from the Model's LatLong.
     * Causes the view to repaint.
     * @param units
     */
    public void setDistanceUnits(Unit<Length> units) {
        Unit<Length> old = c_distanceUnits;
        c_distanceUnits = units;

        firePropertyChange(PROP_DISTANCE_UNITS, old, c_distanceUnits);
    }

    /**
     * Some views display the distance of the station.  Returns the units used 
     * to display the distance.
     * @return
     */
    public Unit<Length> getDistanceUnits() {
        return c_distanceUnits;
    }

    /**
     *
     * @param distance
     */
    public void setDistance(Measurable<Length> distance) {
        Measurable<Length> old = c_distance;
        c_distance = distance;
        firePropertyChange(PROP_DISTANCE, old, c_distance);
    }

    /**
     *
     * @return
     */
    public Measurable<Length> getDistance() {
        return c_distance;
    }

    /**
     *
     * @param type
     */
    public void setFileType(WepsFileChooser.Filetype type) {
        WepsFileChooser.Filetype old = c_fileType;
        c_fileType = type;
        firePropertyChange(PROP_FILE_TYPE, old, c_fileType);
    }

    /**
     *
     * @return
     */
    public WepsFileChooser.Filetype getFileType() {
        return c_fileType;
    }

    /**
     *
     * @param choices
     */
    public void setStationChoices(Station[] choices) {
        Station[] old = c_stationChoices;
        c_stationChoices = choices;
        firePropertyChange(PROP_STATION_CHOICES, old, c_stationChoices);
    }

    /**
     *
     * @return
     */
    public Station[] getStationChoices() {
        return c_stationChoices != null ? c_stationChoices : new Station[0];
    }

    /**
     *
     * @param text
     */
    public void setText(String text) {
        String old = c_text;
        c_text = text;
        firePropertyChange(PROP_TEXT, old, c_text);
    }

    /**
     *
     * @return
     */
    public String getText() {
        return c_text;
    }

    /**
     *
     * @param station
     */
    public void setSelectedStation(Station station) {
        Station old = c_selectedStation;
        c_selectedStation = station;
        firePropertyChange(PROP_SELECTED_STATION, old, c_selectedStation);
    }

    /**
     *
     * @return
     */
    public Station getSelectedStation() {
        return c_selectedStation;
    }

    /**
     *
     * @param label
     */
    public void setLabel(JLabel label) {
        JLabel old = c_label;
        c_label = label;
        firePropertyChange(PROP_LABEL, old, c_label);
    }

    /**
     *
     * @return
     */
    public JLabel getLabel() {
        return c_label;
    }

    /**
     *
     * @param label
     */
    public void setSubLabel(JLabel label) {
        JLabel old = c_subLabel;
        c_subLabel = label;
        firePropertyChange(PROP_LABEL, old, c_subLabel);
    }

    /**
     *
     * @return
     */
    public JLabel getSubLabel() {
        return c_subLabel;
    }

    /**
     *
     * @param view
     */
    public void setView(View view) {
/**
                 * Note:  Assertions are not enabled.  These will be useless items
                 * unless assertions are enabled.  Thus, they will be commented out unless
                 * the user wishes to enable specific assertions (feed the virtual machine 
                 * the -ea argument).
                 */
//        assert view != null;
        View old = c_view;
        c_view = view;

        if (old == c_view) {
            return; //prevent duplicated executions
        }

        //uninstall the old view
        if (old != null) {
            old.uninstall(this);
        }

        //remove any old views
        viewPanel.removeAll();

        //install the new view
        if (c_view != null) {
            Component viewComponent = c_view.install(this);
            viewComponent.setEnabled(isEnabled());
            viewPanel.add(viewComponent, BorderLayout.CENTER);
            viewPanel.validate();
        }

        firePropertyChange(PROP_VIEW, old, c_view);
    }

    /**
     *
     * @return
     */
    public View getView() {
        return c_view;
    }

    /**
     *
     * @param active
     */
    public void setCircleActive(boolean active) {
        circle.setVisible(active);
        circle.setActive(active);
    }

    /**
     *
     * @param enabled
     */
    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        moreButton.setEnabled(enabled);
        for (Component child : viewPanel.getComponents()) {
            child.setEnabled(enabled);
        }
    }

    //view enum, allows us to be typesafe about setting options.
    /**
     *
     */
    public static enum View implements ViewHandler {

        /**
         *
         */
        Choice(StationChoiceView.class),
        /**
         *
         */
        File(StationFileView.class),
        /**
         *
         */
        Label(StationLabelView.class),
        /**
         *
         */
        Text(StationTextView.class);

        private final Class<? extends ViewHandler> c_handlerClass;
        private final Map<StationChooser, ViewHandler> c_handlers;

        View(Class<? extends ViewHandler> handler) {
            c_handlerClass = handler;
            c_handlers = new HashMap<StationChooser, ViewHandler>();
        }

        private synchronized ViewHandler handler(StationChooser chooser) {
            ViewHandler handler = c_handlers.get(chooser);
            if (handler == null) {
                try {
                    handler = c_handlerClass.newInstance();
                    c_handlers.put(chooser, handler);
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

            return handler;
        }

        @Override
        public Component install(StationChooser chooser) {
            return handler(chooser).install(chooser);
        }

        @Override
        public void uninstall(StationChooser chooser) {
            handler(chooser).uninstall(chooser);
        }

    }

    /**
     *
     */
    public interface ViewHandler {

        /**
         * Install the view on the for a StationChooser.  Now is the time to add
         * a PropertyChangeListener to the chooser.
         * @param chooser
         * @return Component used as the view.
         */
        public Component install(StationChooser chooser);

        /**
         * Clean up.  Now is the time to remove a PropertyChangeListener from the
         * chooser.
         * @param chooser
         */
        public void uninstall(StationChooser chooser);

    }
}
