/*
 * MapViewer.java
 *
 * Created on Jun 30, 2009, 4:14:25 PM
 */
package usda.weru.gis.gui;

import com.vividsolutions.jts.geom.Coordinate;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.measure.unit.NonSI;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.geotools.data.DataStore;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.map.FeatureLayer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.styling.Style;
import org.jscience.geography.coordinates.LatLong;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.geometry.primitive.Point;
import org.openide.util.Exceptions;
import usda.weru.gis.GISData;
import usda.weru.gis.GISLookup;
import usda.weru.gis.GISUtil;
import usda.weru.gis.MapData;
import usda.weru.gis.data.CropManagementZone;
import usda.weru.gis.data.Territory;
import usda.weru.weps.location.CligenDataModel;
import usda.weru.weps.location.Station;
import usda.weru.weps.location.WindgenDataModel;

/**
 *
 * @author Joseph A. Levin <joelevin@weru.ksu.edu>
 */
public class MapViewer extends javax.swing.JFrame {

    private static final long serialVersionUID = 1L;

    private final MapController c_controller;
    private LatLong c_selectedLatLong;

    /**
     *
     */
    public static final String PROP_SELECTEDLATLONG = "selectedLatLong";
    private FeatureLayer c_currentLocationLayer;

    private static Rectangle c_restoreBounds;
    private int c_restoreState;

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

        demoWingen.setVisible(false);
        demoWingenLabel.setVisible(false);
        demoCligen.setVisible(false);
        demoCligenLabel.setVisible(false);

        // loading, restore size and location if possible
        Rectangle bounds;
        int state;
        synchronized (MapViewer.class) {
            bounds = c_restoreBounds;
            state = c_restoreState;
        }
        if (bounds != null) {
            setBounds(bounds);
            setExtendedState(state);
        }

        // when a window is closing, save its state
        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                synchronized (MapViewer.class) {
                    c_restoreBounds = getBounds();
                    c_restoreState = getExtendedState();
                }
            }

        });

        c_controller = mapPane.getMapLayer();

        // selection handler
        MouseAdapter selection = new InternalSelectionHandler();
        c_controller.addMouseListener(selection);
        c_controller.addMouseMotionListener(selection);

        setMapContent(createDefaultContent());
    }

    LayerTableModel c_model;

    private void setMapContent(MapContent map) {
        try {
            // wait for the 'GIS Map Loader' thread to finish with 'map'
            synchronized (map) {
                map.wait(3000);
                mapPane.getMapLayer().setMapContent(map);

                c_model = new LayerTableModel(map);
                layerTable.setModel(c_model);
                // size the left column
                layerTable.getColumnModel().getColumn(0).setMaxWidth(45);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    private MapContent getMapContent() {
        return mapPane.getMapLayer().getMapContent();
    }

    /**
     * creates the red crosshairs at the center of the map view
     * @param location the center of the map viewer
     */
    public void setCurrentLocationMarker(final LatLong location) {
        FeatureLayer old = c_currentLocationLayer;
        c_currentLocationLayer = createLatLongMarkerLayer(location);
        if (old != null) {
            getMapContent().removeLayer(old);
        }

        getMapContent().addLayer(c_currentLocationLayer);
    }

    /*
     * returns a layer with one thing on it: the red crosshairs
     */
    private FeatureLayer createLatLongMarkerLayer(LatLong location) {

        Coordinate center = GISUtil.toCoordinate(location);
        System.out.println("Center: " + center);

        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("markerType");
        typeBuilder.setNamespaceURI("usda.weru.gis");

        typeBuilder.add("location", Point.class, DefaultGeographicCRS.WGS84);
        typeBuilder.setDefaultGeometry("location");

        typeBuilder.add("name", String.class);

        SimpleFeatureType markerType = typeBuilder.buildFeatureType();

        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(markerType);

        builder.add(new com.vividsolutions.jts.geom.GeometryFactory().createPoint(center));
        builder.add("Current LatLong");

        DefaultFeatureCollection collection = new DefaultFeatureCollection("latlongMarker", markerType);
        SimpleFeature marker = builder.buildFeature(null);
        collection.add(marker);

        Style style = GISGUIUtil.createCrossPointStyle(Color.RED, Color.PINK);

        //MapLayer layer = new DefaultMapLayer(collection, style, "Current Location");
        FeatureLayer layer = new FeatureLayer(collection, style, "Current Location");

        return layer;
    }

    private FeatureLayer createWindgenStationLayer() {

        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("windgenStation");
        typeBuilder.setNamespaceURI("usda.weru.gis");

        typeBuilder.add("location", Point.class, DefaultGeographicCRS.WGS84);
        typeBuilder.setDefaultGeometry("location");

        typeBuilder.add("name", String.class);

        SimpleFeatureType markerType = typeBuilder.buildFeatureType();

        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(markerType);

        DefaultFeatureCollection collection = new DefaultFeatureCollection("windgenStations", markerType);

        WindgenDataModel data = WindgenDataModel.getInstance();
        for (Station station : data.stations()) {
            builder.reset();
            builder.add(new com.vividsolutions.jts.geom.GeometryFactory().
                    createPoint(GISUtil.toCoordinate(station.getLatLong())));
            builder.add(station.getDisplayName());
            SimpleFeature marker = builder.buildFeature(null);
            // the 'null' here tells the SimpleFeatureBuilder to assign its own id to the SimpleFeature
            collection.add(marker);
        }

        Style style = GISGUIUtil.createCirclePointStyle(5, Color.RED, Color.GRAY);

        FeatureLayer layer = new FeatureLayer(collection, style, "Windgen Stations");
        layer.setVisible(false);

        return layer;
    }

    private FeatureLayer createCligenStationLayer() {

        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("cligenStation");
        typeBuilder.setNamespaceURI("usda.weru.gis");

        typeBuilder.add("location", Point.class, DefaultGeographicCRS.WGS84);
        typeBuilder.setDefaultGeometry("location");

        typeBuilder.add("name", String.class);

        SimpleFeatureType markerType = typeBuilder.buildFeatureType();

        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(markerType);

        DefaultFeatureCollection collection = new DefaultFeatureCollection("cligenStations", markerType);

        CligenDataModel data = CligenDataModel.getInstance();
        for (Station station : data.stations()) {
            builder.reset();
            builder.add(new com.vividsolutions.jts.geom.GeometryFactory()
                    .createPoint(GISUtil.toCoordinate(station.getLatLong())));
            builder.add(station.getDisplayName());
            SimpleFeature marker = builder.buildFeature(null);
            collection.add(marker);
        }

        Style style = GISGUIUtil.createCirclePointStyle(5, Color.BLUE, Color.GRAY);

        FeatureLayer layer = new FeatureLayer(collection, style, "Cligen Stations");
        layer.setVisible(false);

        return layer;
    }

    private MapContent createDefaultContent() {
        //final MapContent content = new MapContent(new MapContext(DefaultGeographicCRS.WGS84));
        final MapContent content = new MapContent();
        Thread task = new Thread("GIS Map Loader") {

            @Override
            public void run() {

                GISData data = GISData.getInstance();

                try {
                    synchronized (content) {
                        // each iteration of this loop adds an entry to the list of layers,
                        // with checkmarks to toggle visibility
                        for (Name name : data.getNames()) {
                            DataStore store = data.dataStore(name);
                            // hack for visibility
                            boolean visible = MapData.isLayerVisibleDefault(name);
                            ArrayList<FeatureLayer> toAdd = getLayers(store, visible);
                            content.addLayers(toAdd);
                        }

                        /**
                         * Note: Assertions were dropped here as no assertions are enabled.
                         * This appears to work fine, and assertions appear to have
                         * only been present for testing purposes.
                         * 
                         * If they were necessary:  we don't want to enable assertions,
                         * thus to replicate their affect:
                         * 
                         * if(!content.addLayer(createWindgenStationLayer())) throw new Exception("Windgen Layer not generated in MapViewer.java.");
                         * if(!content.addLayer(createCligenStationLayer())) throw new Exception("Cligen Layer not generated in MapViewer.java.");
                         */
                        content.addLayer(createWindgenStationLayer());
                        content.addLayer(createCligenStationLayer());
                        content.notifyAll();
                    }
                    while (c_currentLocationLayer == null || !content.layers().contains(c_currentLocationLayer)) {
                        Thread.yield();
                    }
                    synchronized (content) {
                        // this delay prevents a deadlock condition with another thread
                        Thread.sleep(100);
                        content.moveLayer(content.layers().indexOf(c_currentLocationLayer), content.layers().size() - 1);
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

        };

        task.start();
        return content;
    }

    private static ArrayList<FeatureLayer> getLayers(DataStore store, boolean visible) {
        MapData test = new MapData();
        ArrayList<FeatureLayer> layers = new ArrayList<FeatureLayer>();

        try {
            for (String typeName : store.getTypeNames()) {
                SimpleFeatureSource fs = store.getFeatureSource(typeName);

                FeatureLayer fl = test.createLayer(fs);
                fl.setVisible(visible);
                layers.add(fl);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return layers;
    }

    /**
     *
     * @return
     */
    public MapController getMapController() {
        return mapPane.getMapLayer();
    }

    /**
     *
     * @return
     */
    public LatLong getSelectedLatLong() {
        return c_selectedLatLong;
    }

    /**
     *
     * @param latlong
     */
    public void setSelectedLatLong(LatLong latlong) {
        LatLong old = c_selectedLatLong;
        c_selectedLatLong = latlong;
        firePropertyChange(PROP_SELECTEDLATLONG, old, c_selectedLatLong);
    }

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

        splitPanel = new javax.swing.JSplitPane();
        sidePanel = new javax.swing.JPanel();
        tableScrollPanel = new javax.swing.JScrollPane();
        layerTable = new javax.swing.JTable();
        demoCountryLabel = new javax.swing.JLabel();
        demoStateLabel = new javax.swing.JLabel();
        demoCountyLabel = new javax.swing.JLabel();
        demoCMZLabel = new javax.swing.JLabel();
        demoWingenLabel = new javax.swing.JLabel();
        demoCligenLabel = new javax.swing.JLabel();
        demoCMZ = new javax.swing.JLabel();
        demoCountry = new javax.swing.JLabel();
        demoState = new javax.swing.JLabel();
        demoCounty = new javax.swing.JLabel();
        demoWingen = new javax.swing.JLabel();
        demoCligen = new javax.swing.JLabel();
        demoPositionLabel = new javax.swing.JLabel();
        demoPosition = new javax.swing.JLabel();
        mainPanel = new javax.swing.JPanel();
        statusBar = new javax.swing.JPanel();
        statusLabel = new javax.swing.JLabel();
        latLonField = new javax.swing.JFormattedTextField();
        mapPane = new usda.weru.gis.gui.MapPane();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("usda/weru/gis/gui/Bundle"); // NOI18N
        setTitle(bundle.getString("MapViewer.title")); // NOI18N

        splitPanel.setDividerLocation(200);

        layerTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN);
        layerTable.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                layerTableMouseClicked(evt);
            }
        });
        tableScrollPanel.setViewportView(layerTable);

        demoCountryLabel.setFont(demoCountryLabel.getFont().deriveFont(demoCountryLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCountryLabel.setText(bundle.getString("MapViewer.demoCountryLabel.text")); // NOI18N

        demoStateLabel.setFont(demoStateLabel.getFont().deriveFont(demoStateLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoStateLabel.setText(bundle.getString("MapViewer.demoStateLabel.text")); // NOI18N

        demoCountyLabel.setFont(demoCountyLabel.getFont().deriveFont(demoCountyLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCountyLabel.setText(bundle.getString("MapViewer.demoCountyLabel.text")); // NOI18N

        demoCMZLabel.setFont(demoCMZLabel.getFont().deriveFont(demoCMZLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCMZLabel.setText(bundle.getString("MapViewer.demoCMZLabel.text")); // NOI18N

        demoWingenLabel.setFont(demoWingenLabel.getFont().deriveFont(demoWingenLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoWingenLabel.setText(bundle.getString("MapViewer.demoWingenLabel.text")); // NOI18N

        demoCligenLabel.setFont(demoCligenLabel.getFont().deriveFont(demoCligenLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCligenLabel.setText(bundle.getString("MapViewer.demoCligenLabel.text")); // NOI18N

        demoCMZ.setFont(demoCMZ.getFont().deriveFont(demoCMZ.getFont().getStyle() & ~java.awt.Font.BOLD));
        demoCMZ.setText(bundle.getString("MapViewer.demoCMZ.text")); // NOI18N

        demoCountry.setFont(demoCountry.getFont().deriveFont(demoCountry.getFont().getStyle() & ~java.awt.Font.BOLD));
        demoCountry.setText(bundle.getString("MapViewer.demoCountry.text")); // NOI18N

        demoState.setFont(demoState.getFont().deriveFont(demoState.getFont().getStyle() & ~java.awt.Font.BOLD));
        demoState.setText(bundle.getString("MapViewer.demoState.text")); // NOI18N

        demoCounty.setFont(demoCounty.getFont().deriveFont(demoCounty.getFont().getStyle() & ~java.awt.Font.BOLD));
        demoCounty.setText(bundle.getString("MapViewer.demoCounty.text")); // NOI18N

        demoWingen.setFont(demoWingen.getFont().deriveFont(demoWingen.getFont().getStyle() & ~java.awt.Font.BOLD));
        demoWingen.setText(bundle.getString("MapViewer.demoWingen.text")); // NOI18N

        demoCligen.setFont(demoCligen.getFont().deriveFont(demoCligen.getFont().getStyle() & ~java.awt.Font.BOLD));
        demoCligen.setText(bundle.getString("MapViewer.demoCligen.text")); // NOI18N

        demoPositionLabel.setFont(demoPositionLabel.getFont().deriveFont(demoPositionLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoPositionLabel.setText(bundle.getString("MapViewer.demoPositionLabel.text")); // NOI18N

        demoPosition.setFont(demoPosition.getFont().deriveFont(demoPosition.getFont().getStyle() & ~java.awt.Font.BOLD));
        demoPosition.setText(bundle.getString("MapViewer.demoPosition.text")); // NOI18N

        javax.swing.GroupLayout sidePanelLayout = new javax.swing.GroupLayout(sidePanel);
        sidePanel.setLayout(sidePanelLayout);
        sidePanelLayout.setHorizontalGroup(
            sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(sidePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(sidePanelLayout.createSequentialGroup()
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(demoCountryLabel)
                            .addComponent(demoStateLabel)
                            .addComponent(demoCMZLabel)
                            .addComponent(demoCountyLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(demoCounty, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)
                            .addComponent(demoCMZ, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)
                            .addComponent(demoState, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)
                            .addComponent(demoCountry, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)))
                    .addGroup(sidePanelLayout.createSequentialGroup()
                        .addComponent(demoPositionLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(demoPosition, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, sidePanelLayout.createSequentialGroup()
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(demoWingenLabel)
                            .addComponent(demoCligenLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(demoCligen, javax.swing.GroupLayout.DEFAULT_SIZE, 126, Short.MAX_VALUE)
                            .addComponent(demoWingen, javax.swing.GroupLayout.DEFAULT_SIZE, 126, Short.MAX_VALUE))))
                .addContainerGap())
            .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(sidePanelLayout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(tableScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE)
                    .addContainerGap()))
        );
        sidePanelLayout.setVerticalGroup(
            sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(sidePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoPositionLabel)
                    .addComponent(demoPosition))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCountryLabel)
                    .addComponent(demoCountry))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoStateLabel)
                    .addComponent(demoState))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCountyLabel)
                    .addComponent(demoCounty))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCMZLabel)
                    .addComponent(demoCMZ))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoWingenLabel)
                    .addComponent(demoWingen))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCligenLabel)
                    .addComponent(demoCligen))
                .addContainerGap(353, Short.MAX_VALUE))
            .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, sidePanelLayout.createSequentialGroup()
                    .addContainerGap(261, Short.MAX_VALUE)
                    .addComponent(tableScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 226, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addContainerGap()))
        );

        splitPanel.setLeftComponent(sidePanel);

        statusBar.setBorder(javax.swing.BorderFactory.createEtchedBorder());

        statusLabel.setText(bundle.getString("MapViewer.statusLabel.text")); // NOI18N

        latLonField.setEditable(false);
        latLonField.setFormatterFactory(new LatLonFormatterFactory());
        latLonField.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        latLonField.setText(bundle.getString("MapViewer.latLonField.text")); // NOI18N
        latLonField.setPreferredSize(new java.awt.Dimension(30, 20));

        javax.swing.GroupLayout statusBarLayout = new javax.swing.GroupLayout(statusBar);
        statusBar.setLayout(statusBarLayout);
        statusBarLayout.setHorizontalGroup(
            statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusBarLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(latLonField, javax.swing.GroupLayout.PREFERRED_SIZE, 135, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
        statusBarLayout.setVerticalGroup(
            statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, statusBarLayout.createSequentialGroup()
                .addGap(0, 0, 0)
                .addGroup(statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(statusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
                    .addComponent(latLonField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
        );

        mapPane.setBackground(new java.awt.Color(255, 255, 255));
        mapPane.setOpaque(true);

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(statusBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(mapPane, javax.swing.GroupLayout.DEFAULT_SIZE, 465, Short.MAX_VALUE)
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
                .addComponent(mapPane, javax.swing.GroupLayout.DEFAULT_SIZE, 467, Short.MAX_VALUE)
                .addGap(0, 0, 0)
                .addComponent(statusBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        splitPanel.setRightComponent(mainPanel);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(splitPanel)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(splitPanel)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void layerTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_layerTableMouseClicked

    }//GEN-LAST:event_layerTableMouseClicked

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new MapViewer().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel demoCMZ;
    private javax.swing.JLabel demoCMZLabel;
    private javax.swing.JLabel demoCligen;
    private javax.swing.JLabel demoCligenLabel;
    private javax.swing.JLabel demoCountry;
    private javax.swing.JLabel demoCountryLabel;
    private javax.swing.JLabel demoCounty;
    private javax.swing.JLabel demoCountyLabel;
    private javax.swing.JLabel demoPosition;
    private javax.swing.JLabel demoPositionLabel;
    private javax.swing.JLabel demoState;
    private javax.swing.JLabel demoStateLabel;
    private javax.swing.JLabel demoWingen;
    private javax.swing.JLabel demoWingenLabel;
    private javax.swing.JFormattedTextField latLonField;
    private javax.swing.JTable layerTable;
    private javax.swing.JPanel mainPanel;
    private usda.weru.gis.gui.MapPane mapPane;
    private javax.swing.JPanel sidePanel;
    private javax.swing.JSplitPane splitPanel;
    private javax.swing.JPanel statusBar;
    private javax.swing.JLabel statusLabel;
    private javax.swing.JScrollPane tableScrollPanel;
    // End of variables declaration//GEN-END:variables

    private class InternalSelectionHandler extends MouseAdapter {

        private Coordinate translate(MouseEvent e) {

            Coordinate center = c_controller.getCenter();
            double zoom = c_controller.getZoomFactor();
            double cx = e.getX() - (mapPane.getWidth() / 2);
            double cy = e.getY() - (mapPane.getHeight() / 2);
            double dx = cx / zoom;
            double dy = cy / zoom;
            return new Coordinate(center.x + dx, center.y - dy);
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
                Coordinate select = translate(e);
                c_controller.setCenter(select);
                LatLong latlong = GISUtil.toLatLong(select);
                setSelectedLatLong(latlong);
            } else {
                Coordinate select = translate(e);
                LatLong latlong = GISUtil.toLatLong(select);
                GISUtil.toLatLong(select);
                demoMetaData(latlong);
            }
        }

        @Override
        public void mouseExited(MouseEvent e) {
            latLonField.setValue(c_controller.getCenter());
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            Coordinate coord = translate(e);
            latLonField.setValue(coord);
        }
    }
    DemoMetaDataSwingWorker c_demo;

    /**
     *
     * @param latlong
     */
    public synchronized void demoMetaData(LatLong latlong) {
        if (c_demo != null) {
            c_demo.cancel(false);
        }

        c_demo = new DemoMetaDataSwingWorker(latlong);
        c_demo.execute();

    }

    private class DemoMetaDataSwingWorker extends SwingWorker<Object, Runnable> {

        private final LatLong c_latlong;

        public DemoMetaDataSwingWorker(LatLong latlong) {
            c_latlong = latlong;
        }

        @Override
        protected Object doInBackground() throws Exception {
            Thread.sleep(1200);
            if (isCancelled()) {
                return null;
            }
            publish(demoCountry, "");
            publish(demoState, "");
            publish(demoCounty, "");
            publish(demoCMZ, "");
            //publish(demoSoils, "");
            publish(demoWingen, "");
            publish(demoCligen, "");

            //position
            try {
                NumberFormat format = new DecimalFormat("0.00000");
                publish(demoPosition, format.format(c_latlong.longitudeValue(NonSI.DEGREE_ANGLE))
                        + ", " + format.format(c_latlong.latitudeValue(NonSI.DEGREE_ANGLE)));
                if (isCancelled()) {
                    return null;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (isCancelled()) {
                return null;
            }
            //end position

            //territory
            List<Territory> territories = lookupValues(Territory.class);
            for (Territory territory : territories) {
                switch (territory.getType()) {
                    case Country:
                        publish(demoCountry, territory.getDisplayName());
                        break;
                    case State:
                        publish(demoState, territory.getDisplayName());
                        break;
                    case County:
                        publish(demoCounty, territory.getDisplayName());
                        publish(demoCountyLabel, territory.getTypeDescription() + ":");
                        break;
                }
            }
            if (isCancelled()) {
                return null;
            }
            //end territory

            //CMZ
            CropManagementZone cmz = lookupValue(CropManagementZone.class);
            if (cmz != null) {
                publish(demoCMZ, Float.toString(cmz.getId()));
            } else {
                publish(demoCMZ, "");
            }
            if (isCancelled()) {
                return null;
            }
            //end cmz

            return null;
        }

        private <T> T lookupValue(Class<T> type) {
            GISData data = GISData.getInstance();
            GISLookup<T> lookup = data.getLookup(type);
            List<T> results = null;
            if (lookup != null) {
                results = lookup.lookup(c_latlong);
            }

            if (results != null && !results.isEmpty()) {
                return results.get(0);
            } else {
                return null;
            }

        }

        private <T> List<T> lookupValues(Class<T> type) {
            GISData data = GISData.getInstance();
            GISLookup<T> lookup = data.getLookup(type);
            List<T> results = null;
            if (lookup != null) {
                results = lookup.lookup(c_latlong);
            }
            if (results != null) {
                return results;
            } else {
                return Collections.emptyList();
            }
        }

        private void publish(final JLabel label, final String text) {
            Runnable runnable = new Runnable() {

                @Override
                public void run() {
                    label.setText(text);
                }
            };
            publish(runnable);
        }

        @Override
        protected void process(List<Runnable> chunks) {
            
            for (Runnable runnable : chunks) {
                if (DemoMetaDataSwingWorker.this.isCancelled()) {
                    return;
                }
                runnable.run();
            }
        }
    }
}
