/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * StationNearestView.java
 *
 * Created on Aug 5, 2009, 11:56:48 AM
 */
package usda.weru.weps.location.chooser;

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import javax.measure.Measurable;
import javax.measure.quantity.Length;
import javax.measure.unit.Unit;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import org.jscience.geography.coordinates.LatLong;
import usda.weru.gis.GISUtil;
import usda.weru.weps.location.Station;

/**
 *
 * @author Joseph Levin <joelevin@weru.ksu.edu>
 */
public class StationChoiceView extends JPanel implements StationChooser.ViewHandler, ListCellRenderer, ItemListener, PropertyChangeListener {

    private StationChooser c_chooser;
    private InternalComboBoxModel c_model;
    private NumberFormat c_format = new DecimalFormat("0.0");

    /** Creates new form StationNearestView */
    public StationChoiceView() {
        initComponents();
        combo.setRenderer(this);
        c_model = new InternalComboBoxModel();
        combo.setModel(c_model);

    }

    /** 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() {

        renderer = new javax.swing.JPanel();
        stationName = new javax.swing.JLabel();
        stationDistance = new javax.swing.JLabel();
        combo = new javax.swing.JComboBox();

        stationName.setFont(stationName.getFont().deriveFont(stationName.getFont().getStyle() & ~java.awt.Font.BOLD));
        stationName.setText("displayName");
        stationName.setPreferredSize(null);

        stationDistance.setFont(stationDistance.getFont().deriveFont(stationDistance.getFont().getStyle() & ~java.awt.Font.BOLD));
        stationDistance.setText("100");

        javax.swing.GroupLayout rendererLayout = new javax.swing.GroupLayout(renderer);
        renderer.setLayout(rendererLayout);
        rendererLayout.setHorizontalGroup(
            rendererLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, rendererLayout.createSequentialGroup()
                .addComponent(stationName, javax.swing.GroupLayout.DEFAULT_SIZE, 161, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(stationDistance)
                .addContainerGap())
        );
        rendererLayout.setVerticalGroup(
            rendererLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(stationDistance, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(stationName, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(combo, 0, 165, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(combo, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
        );
    }// </editor-fold>//GEN-END:initComponents

    @Override
    public Component install(StationChooser chooser) {
        assert EventQueue.isDispatchThread();
        c_chooser = chooser;        
        enqueueUpdate();
        updateDistance();
        c_chooser.addPropertyChangeListener(this);
        return this;
    }

    @Override
    public void uninstall(StationChooser chooser) {
        JLabel subLabel = c_chooser.getSubLabel();
        if(subLabel != null){
            subLabel.setText("");
        }
        c_chooser.removePropertyChangeListener(this);
        c_chooser = null;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (StationChooser.PROP_DISTANCE_UNITS.equals(evt.getPropertyName())) {
            //only need to repaint because the cell renderer will calculate the distance on each paint
            repaint();
            updateDistance();
        }
        else if (StationChooser.PROP_LATLONG.equals(evt.getPropertyName())) {
            //only need to repaint because the cell renderer will calculate the distance on each paint
            repaint();
        }
        else if(StationChooser.PROP_STATION_CHOICES.equals(evt.getPropertyName())){
            //the choices changed, upate the model
            enqueueUpdate();
        }
        else if (StationChooser.PROP_SELECTED_STATION.equals(evt.getPropertyName())){
            //the selected station changed'
            setStation(c_chooser.getSelectedStation());
        }
        else if(StationChooser.PROP_DISTANCE.equals(evt.getPropertyName())){
            updateDistance();
        }

    }

    private void updateDistance(){
        JLabel subLabel = c_chooser.getSubLabel();
            if(subLabel != null){
                Measurable<Length> distance = c_chooser.getDistance();
                Unit<Length> unit = c_chooser.getDistanceUnits();
                if(distance != null && unit != null){
                    subLabel.setText("< " + c_format.format(distance.doubleValue(unit)) + " " + unit.toString());

                    return;
                }
                else{
                    subLabel.setText("");
                }
            }
            
    }

    public void setStation(Station station) {
        assert EventQueue.isDispatchThread();        
        setItemListenerActive(false);
        if (station != null) {
            combo.setSelectedItem(station);
        } else {
            combo.setSelectedIndex(-1);
        }
        setItemListenerActive(true);
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        combo.setEnabled(enabled);
    }

    /**
     * Enables or disables the listener so that changes can be made without
     * notifying the whole world.
     * @param active
     */
    private void setItemListenerActive(boolean active) {
        if (active) {
            combo.addItemListener(this);
        } else {
            combo.removeItemListener(this);
        }
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        if (ItemEvent.SELECTED == e.getStateChange()) {
            c_chooser.setSelectedStation((Station) combo.getSelectedItem());
        }
    }

    /**
     * Schedules an update of the data in the backing model.  Current implementation
     * just executes the update.  Future updates will collapse requests within a
     * window and have the work done on a swing worker.
     */
    private synchronized void enqueueUpdate() {
        c_model.update();
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JComboBox combo;
    private javax.swing.JPanel renderer;
    private javax.swing.JLabel stationDistance;
    private javax.swing.JLabel stationName;
    // End of variables declaration//GEN-END:variables

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        if (value != null && value instanceof Station) {
            Station station = (Station) value;

            if (isSelected) {
                renderer.setBackground(list.getSelectionBackground());
            } else {
                //list is coming up gray, so use the combo
                renderer.setBackground(Color.WHITE);
            }

            stationName.setText(station.getDisplayName());

            double distanceMeters = 0;
            //TODO: can the chooser actually ever be null?
            if (c_chooser != null && c_chooser.getLatLong() != null) {
                //TODO: perhaps cache the distances?  Is this a costly calculation?
                LatLong latlong1 = c_chooser.getLatLong();
                LatLong latlong2 = station.getLatLong();
                
                Measurable<Length> distance = GISUtil.distanceBetweenCoordinates(latlong1, latlong2);
                
                if(distance != null && c_chooser.getDistanceUnits() != null){
                    distanceMeters = distance.doubleValue(c_chooser.getDistanceUnits());
                    stationDistance.setText(c_format.format(distanceMeters));
                }
                else{
                    stationDistance.setText("");
                }
            } else {
                stationDistance.setText("");
            }
        } else {
            stationName.setText("");
            stationDistance.setText("");
        }

        if(index == -1){
            Unit units = c_chooser.getDistanceUnits();
            stationDistance.setText(stationDistance.getText() + " " + (units != null ? units.toString() : ""));
        }
        return renderer;
    }

    private class InternalComboBoxModel extends DefaultComboBoxModel {

        public InternalComboBoxModel() {
        }

        public void update() {
            setItemListenerActive(false);
            try {
                combo.setEnabled(false);                
                removeAllElements();                
                for (Station station : c_chooser.getStationChoices()) {
                    addElement(station);
                }

                //The station model always dictates what is selected.
                setSelectedItem(c_chooser.getSelectedStation());
                

            } finally {
                combo.setEnabled(true);
                setItemListenerActive(true);
            }


        }
    }
}
