/*
 * SiteChooser.java
 *
 * Created on Sep 22, 2009, 5:02:41 PM
 */
package usda.weru.weps.location.chooser;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.Beans;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import org.apache.log4j.Logger;
import usda.weru.gis.GISData;
import usda.weru.gis.GISLookup;
import usda.weru.util.Caster;
import usda.weru.util.WepsComboBox;
import usda.weru.weps.location.Site;

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

    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = Logger.getLogger(SiteChooser.class);

    /**
     *
     */
    public static final String PROP_ROOT_SITE = "rootSite";

    /**
     *
     */
    public static final String PROP_SELECTED_SITE = "selectedSite";

    private Site<?> c_rootSite;
    private final ChainedComboBox c_rootComboBox;

    private Site<?> c_selectedSite;

    /** Creates new form SiteChooser */
    public SiteChooser() {
        initComponents();

        c_rootComboBox = new ChainedComboBox();
        add(c_rootComboBox);
        //c_rootComboBox.setEnabled(false);

        if (!Beans.isDesignTime()) {
            setRootSite(null);
        }

    }

    /**
     *
     * @return
     */
    @Override
    public Dimension getPreferredSize() {
        Dimension temp = super.getPreferredSize();
        return new Dimension(0, temp.height);

    }

    /**
     *
     * @return
     */
    @Override
    public Dimension getMinimumSize() {
        Dimension temp = super.getMinimumSize();
        return new Dimension(0, temp.height);
    }

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

        labelPanel = new javax.swing.JPanel();

        labelPanel.setLayout(new java.awt.GridLayout(0, 1));

        setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD));
        setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
    }// </editor-fold>//GEN-END:initComponents

    /**
     *
     * @param root
     */
    public void setRootSite(Site<?> root) {
        Site<?> old = c_rootSite;

        c_rootSite = root;

        //TODO: update the display choices
        c_rootComboBox.setModel(new SiteComboBoxModel(getChildren(root)));
        c_rootComboBox.setSelectedIndex(-1);

        firePropertyChange(PROP_ROOT_SITE, old, c_rootSite);
    }

    /**
     *
     * @return
     */
    public Site<?> getRootSite() {
        return c_rootSite;
    }

    /**
     *
     * @param site
     */
    public void setSelectedSite(Site<?> site) {

        try {

            if (site == null) {
                c_rootComboBox.setSelectedIndex(-1);
                return;
            }

            LinkedList<Site<?>> chain = new LinkedList<>();
            //need to build the chain from the site
            Site<?> temp = site;
            while (temp != null) {
                if (temp.equals(c_rootSite)) {
                    //don't go above the root site
                    break;
                }
                chain.add(0, temp);
                temp = temp.getParent();
            }
/**
                 * 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 (!chain.isEmpty());

            //the top of the chain should be a child of the root site
            if (c_rootSite != null) {
                boolean isChild = false;

                for (Site<?> child : c_rootSite.getSubDivisions()) {
                    if (child.equals(chain.getFirst())) {
                        isChild = true;
                        break;
                    }
                }

                if (!isChild) {
                    LOGGER.warn("Unable to set site choice. " + chain.getFirst().getDisplayName()
                            + " is not a division of " + c_rootSite.getDisplayName());
                    c_rootComboBox.setSelectedIndex(-1);
                    return;
                }
            }

            ChainedComboBox combo = c_rootComboBox;

            for (Site<?> choice : chain) {
                combo.setSelectedItem(choice);

                combo = combo.c_downStream;
            }

            if (combo != null) {
                combo.setSelectedItem(null);
            }

        } finally {
            setSelectedSite2(site);
        }

    }

    private void setSelectedSite2(Site<?> site) {
        Site<?> old = c_selectedSite;
        
        c_selectedSite = site;
        firePropertyChange(PROP_SELECTED_SITE, old, c_selectedSite);
    }

    /**
     *
     * @return
     */
    public Site<?> getSelectedSite() {
        return c_selectedSite;
    }

    private Site<?>[] getChildren(Site<?> site) {
        Site<?>[] children;
        if (site != null) {
            children = site.getSubDivisions();
        } else {
            children = Site.countries();
        }

        Arrays.sort(children);

        return children;
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel labelPanel;
    // End of variables declaration//GEN-END:variables

    private class ChainedComboBox extends WepsComboBox<Object> implements ItemListener {

        private static final long serialVersionUID = 1L;

        private ChainedComboBox c_downStream;
        private JLabel c_label;

        public ChainedComboBox() {
            initilize();
        }

        private void initilize() {
            //create and add label
            c_label = new JLabel("Site");
            labelPanel.add(c_label);

            setFont(getFont().deriveFont(Font.PLAIN));
            addItemListener(this);
            setRenderer(getRendererInstance());

        }

        @Override
        public void itemStateChanged(ItemEvent e) {
            //create the down stream
            Site<?> selected = getSelectedSite();
            Site<?>[] children = selected != null ? getChildren(selected) : null;
            if (children != null && children.length > 0) {
                if (c_downStream == null) {
                    addDownStream();
                }

                c_downStream.setModel(new SiteComboBoxModel(children));
                if (children.length == 1) {
                    c_downStream.setSelectedIndex(0);
                } else {
                    c_downStream.setSelectedIndex(-1);
                }

            } else {
                removeDownstream();
            }

            
            if(selected != null)
            {
                try 
                {
                    GISLookup<Site<?>> lookup
                            = GISData.getInstance().<Site<?>>getLookup(Caster.<Class<Site<?>>>cast(Site.class));
                    List<Site<?>> results = lookup.lookup(selected.getLatLong());

                    if (results.size() > 0) 
                    {
                        Comparator<Site<?>> c = new Site.LevelComparator();
                        Collections.sort(results, c);
                        selected = results.get(results.size() - 1);
                        for(int index = 0; index < children.length; index ++)
                        {
                            if(children[index].equals(selected)) c_downStream.setSelectedIndex(index);
                        }
                    } 

                } catch (Exception ec) { LOGGER.warn("Finer granularity not found."); }
            }
            
            setSelectedSite2(selected);

        }

        @Override
        public void setModel(ComboBoxModel<Object> aModel) {
            super.setModel(aModel);
            updateLabel();
        }

        private void addDownStream() {
            c_downStream = new ChainedComboBox();
            SiteChooser.this.add(c_downStream);
        }

        public void removeDownstream() {
            if (c_downStream != null) {
                c_downStream.removeDownstream();

                //remove the label
                labelPanel.remove(c_downStream.c_label);

                SiteChooser.this.remove(c_downStream);
                c_downStream = null;

            }

        }

        private void updateLabel() {
            try {
                if (c_label == null) {
                    return;
                }
                Object o;
                if (getModel().getSize() > 0) {
                    o = getModel().getElementAt(0);
                } else {
                    o = null;
                }

                Site<?> firstSiteInModel = null;
                if (o instanceof Site<?>) {
                    firstSiteInModel = (Site<?>) o;
                }

                String designation = firstSiteInModel != null ? firstSiteInModel.getDesgination().name() : "Site";
                c_label.setText(designation + ":");
            } catch (Exception e) {
                LOGGER.warn("Unable to determine site label.", e);
            }
        }

        public Site<?> getSelectedSite() {
            if (getSelectedItem() instanceof Site<?>) {
                return (Site<?>) getSelectedItem();
            } else {
                return null;
            }
        }

    }

    private class SiteComboBoxModel extends DefaultComboBoxModel<Object> {

        private static final long serialVersionUID = 1L;

        public SiteComboBoxModel(Site<?>[] sites) {
            super(sites);
        }

    }

    private InternalCellRenderer c_renderer;

    private synchronized InternalCellRenderer getRendererInstance() {
        if (c_renderer == null) {
            c_renderer = new InternalCellRenderer();
        }
        return c_renderer;
    }

    private class InternalCellRenderer extends JLabel implements ListCellRenderer<Object> {

        private static final long serialVersionUID = 1L;

        public InternalCellRenderer() {
            setOpaque(true);
        }

        @Override
        public Component getListCellRendererComponent(JList<? extends Object> list,
                Object value, int index, boolean isSelected, boolean cellHasFocus) {
            setBackground(isSelected ? list.getSelectionBackground() : list.getBackground());
            setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());

            if (value instanceof Site) {
                Site<?> site = (Site) value;
                setText(" " + site.getDisplayName().toUpperCase());
            } else {
                setText("");
            }

            return this;
        }

    }

    /**
     *
     * @return
     */
    public JPanel getLabelPanel() {
        return labelPanel;
    }

}
