package usda.weru.weps.location.mode;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import usda.weru.gis.GISData;
import usda.weru.gis.GISLookup;
import usda.weru.gis.data.CligenSelectArea;
import usda.weru.gis.data.SelectArea;
import usda.weru.gis.data.WindgenSelectArea;
import usda.weru.util.LoadingContext;
import usda.weru.util.PropertyVetoRunnableException;
import usda.weru.weps.RunFileBean;
import usda.weru.weps.RunFileBean.StationType;
import usda.weru.weps.location.Station;
import usda.weru.weps.location.chooser.StationChooser;
import usda.weru.weps.location.mode.GISHandler.GISBeanState;
import usda.weru.weps.location.mode.AbstractStationModeHandler.BeanState;

/**
 * 
 * @author Joseph Levin <joelevin@weru.ksu.edu>
 */
public class GISHandler extends AbstractStationModeHandler<GISBeanState> {

    /**
     *
     * @param chooser
     */
    @Override
    protected void setView(StationChooser chooser) {
        chooser.setView(StationChooser.View.Label);
    }

    /**
     *
     * @param bean
     * @param type
     * @return
     */
    @Override
    protected GISBeanState createBeanState(RunFileBean bean, StationType type) {
        return new GISBeanState(bean, type);
    }

    /**
     *
     * @param state
     */
    protected void handleLatLongChange(GISBeanState state) {
        if (!LoadingContext.isLoading()) {
            enqueueValidate(state);
        }
    }

    @Override
    protected void handleValidate(final GISBeanState state, Station oldStation,
            final Station newStation, boolean loading, PropertyChangeEvent event)
            throws PropertyVetoException {
        if (!loading) {
            return;
        }

        if (newStation == null) {
            //TODO: might need to alert the user here?
            return;
        }

        final SelectArea area = getSelectArea(state);

        if (area != null && newStation.equals(area.getStation())) {
            // valid
        } else {
            //send the exception off
            throw new PropertyVetoRunnableException("Station choice is no longer valid.", event, new Runnable() {

                @Override
                public void run() {

                    String validName = (area != null && area.getStation() != null)
                            ? area.getStation().getDisplayName() : "no selection";

                    JOptionPane pane = new JOptionPane("The simulation you are attempting to open uses a "
                            + state.getType().getDisplayName() + " station that is no longer valid. "
                            + newStation.getDisplayName() + " will be substituted with " + validName
                            + ".  Simulation results may be different.", JOptionPane.WARNING_MESSAGE) {
                                private static final long serialVersionUID = 1L;

                                @Override
                                public int getMaxCharactersPerLineCount() {
                                    return 80;
                                }
                            };

                    JDialog dialog = pane.createDialog(state.getType().getDisplayName() + " Station Selection");
                    dialog.setModal(true);
                    dialog.setVisible(true);

                    //set to a valid station choice
                    state.setSelectedStation(area.getStation());
                }
            });
        }
    }

    /**
     * loading the gis data can sometimes take a while
     * @param state
     */
    protected void enqueueValidate(final GISBeanState state) {

        setEnabled(state, true);
        setView(state, StationChooser.View.Label);
        SwingWorker<SelectArea, Object> worker = new SwingWorker<SelectArea, Object>() {

            @Override
            protected SelectArea doInBackground() throws Exception {
                setCircleActive(state, true);
                return getSelectArea(state);
            }

            @Override
            protected void done() {
                try {
                    SelectArea area = get();
                    setCircleActive(state, false);
                    handleSelectArea(state, area);
                    setEnabled(state, true);
                } catch (InterruptedException | ExecutionException e) {
                    //might need to do something here
                }
            }
        };

        worker.execute();

    }

    /**
     *
     * @param s
     * @param enabled
     */
    protected void setEnabled(AbstractStationModeHandler<?>.BeanState s, boolean enabled) {
        Iterator<StationChooser> i = s.choosers();
        while (i.hasNext()) {
            i.next().setEnabled(enabled);
        }
    }

    /**
     *
     * @param s
     * @param active
     */
    protected void setCircleActive(AbstractStationModeHandler<?>.BeanState s, boolean active) {
        Iterator<StationChooser> i = s.choosers();
        while (i.hasNext()) {
            i.next().setCircleActive(active);
        }
    }

    /**
     *
     * @param s
     * @param view
     */
    protected void setView(AbstractStationModeHandler<?>.BeanState s, StationChooser.View view) {
        Iterator<StationChooser> i = s.choosers();
        while (i.hasNext()) {
            i.next().setView(view);
        }
    }

    /**
     *
     * @param s
     * @param text
     */
    protected void setText(AbstractStationModeHandler<?>.BeanState s, String text) {
        Iterator<StationChooser> i = s.choosers();
        while (i.hasNext()) {
            i.next().setText(text);
        }
    }

    /**
     *
     * @param state
     * @param area
     */
    protected void handleSelectArea(GISBeanState state, SelectArea area) {
        if (area != null && area.getType() == SelectArea.Type.Station) {
            setView(state, StationChooser.View.Label);
            if (area.getStation() != state.getSelectedStation()) {
                state.setSelectedStation(area.getStation());
            }
        } else {
            setText(state, "No Station Found");
            setView(state, StationChooser.View.Label);
            state.setSelectedStation(null);
        }
    }

    /**
     *
     * @param state
     * @return
     */
    protected SelectArea getSelectArea(GISBeanState state) {
        //need the lookup into the gis data
        GISLookup<? extends SelectArea> lookup = state.getSelectAreaGISLookup();

        //now if we have a lookup, let's execute the query for the latlong
        if (lookup != null) {
            List<? extends SelectArea> results = lookup.lookup(state.getBean().getLatLong());
            if (results.size() > 0) {
                //returns the first result
                return results.get(0);
            }
        }
        return null;
    }

    @Override
    @Deprecated
    public int getId() {
        return 2;
    }

    /**
     *
     * @return
     */
    @Override
    public String getDisplayName() {
        return "GIS";
    }

    @Override
    public String getName() {
        return "gis";
    }

    /**
     *
     */
    protected class GISBeanState extends AbstractStationModeHandler<?>.BeanState implements PropertyChangeListener {

        /**
         *
         * @param bean
         * @param type
         */
        public GISBeanState(RunFileBean bean, RunFileBean.StationType type) {
            super(bean, type);
        }

        /**
         *
         */
        @Override
        public void install() {
            super.install();
            handleLatLongChange(this);
            getBean().addPropertyChangeListener(RunFileBean.PROP_LATLONG, this);
        }

        /**
         *
         */
        @Override
        public void uninstall() {
            super.uninstall();
            getBean().removePropertyChangeListener(RunFileBean.PROP_LATLONG, this);
        }

        /**
         *
         * @param evt
         */
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
/**
                 * 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 getBean() == evt.getSource() : "BeanState getting events from unexpected source.";
            if (RunFileBean.PROP_LATLONG.equals(evt.getPropertyName())) {
                //lat long changed
                handleLatLongChange(this);
            }
        }

        /**
         *
         * @return
         */
        public GISLookup<? extends SelectArea> getSelectAreaGISLookup() {
            switch (getType()) {
                case Cligen:
                    return GISData.getInstance().getLookup(CligenSelectArea.class);
                case Windgen:
                    return GISData.getInstance().getLookup(WindgenSelectArea.class);
                default:
                    throw new IllegalStateException("not sure why we're here.");
            }
        }
    }
}
