package org.geotools.demo;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureSource;
import org.geotools.demo.postgis.PostGISDialog;
import org.geotools.feature.AttributeType;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureType;
import org.geotools.feature.GeometryAttributeType;
import org.geotools.feature.visitor.FeatureVisitor;
import org.geotools.filter.FilterTransformer;
import org.geotools.filter.text.cql2.CQL;
import org.opengis.filter.Filter;

public class PostGISLab {
    /**
     * Tip: When running from eclipse include the ${file_prompt} as an argument!
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Map connectionProperties = getConnectionProperties(args);
        DataStore dataStore = DataStoreFinder
                .getDataStore(connectionProperties);
        String[] typeNames = dataStore.getTypeNames();
        if (typeNames == null) {
            JOptionPane.showConfirmDialog(null, "Could not conntect");
            System.exit(0);
        }
        JQuery dialog = new JQuery(dataStore);
        dialog.setVisible(true);
        dialog.dispose();
        System.exit(0);
    }

    private static Map getConnectionProperties(String[] args)
            throws IOException {
        PostGISDialog dialog;

        if (args.length == 0) {
            dialog = new PostGISDialog();
        } else {
            File file = new File(args[0]);
            if (!file.exists()) {
                throw new FileNotFoundException(file.getAbsolutePath());
            }
            InputStream input = new FileInputStream(file);
            Properties config = new Properties();
            config.load(input);

            dialog = new PostGISDialog(config);
        }
        dialog.setVisible(true);
        Map properties = dialog.getProperties();
        dialog.dispose();

        if (properties == null) {
            System.exit(0);
        }
        return properties;
    }

    static class JQuery extends JDialog {
        final DataStore dataStore;

        JTextArea query;
        JTextArea show;
        JButton selectButton;
        JButton closeButton;
        JComboBox typeNameSelect;
        JButton schemaButton;

        private JButton filterButton;

        JQuery(DataStore database) throws IOException {
            this.dataStore = database;
            setTitle("Query");
            setModal(true);
            setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);

            JPanel panel = new JPanel();
            panel.setLayout(new GridBagLayout());
            GridBagConstraints c = new GridBagConstraints();
            c.fill = GridBagConstraints.NONE;
            c.anchor = GridBagConstraints.WEST;
            c.insets = new Insets(2, 2, 2, 2);
            c.weightx = 1.0;

            Vector options = new Vector();
            String typeNames[] = dataStore.getTypeNames();
            for (int i = 0; i < typeNames.length; i++) {
                String typeName = typeNames[i];
                options.add(typeName);
            }
            typeNameSelect = new JComboBox(options);
            panel.add(typeNameSelect, c);

            schemaButton = new JButton("Describe Schema");
            schemaButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    displaySchema();
                }
            });
            c.gridwidth = GridBagConstraints.REMAINDER;
            panel.add(schemaButton, c);

            c.weightx = 0.0;
            c.weighty = 0.0;
            query = new JTextArea(4, 80);
            c.fill = GridBagConstraints.BOTH;
            JScrollPane scrollPane1 = new JScrollPane(query);
            scrollPane1.setPreferredSize(query
                    .getPreferredScrollableViewportSize());
            scrollPane1.setMinimumSize(query
                    .getPreferredScrollableViewportSize());
            panel.add(scrollPane1, c);

            c.fill = GridBagConstraints.NONE;
            c.weightx = 0.0;
            c.weighty = 0.0;
            c.gridwidth = GridBagConstraints.RELATIVE;
            selectButton = new JButton("Select Features");
            selectButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    selectFeatures();
                }
            });
            panel.add(selectButton, c);

            c.gridwidth = GridBagConstraints.REMAINDER;
            filterButton = new JButton("CQL to Filter 1.0");
            filterButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    cqlToFilter();
                }
            });
            panel.add(filterButton, c);

            c.fill = GridBagConstraints.BOTH;
            c.weightx = 1.0;
            c.weighty = 1.0;
            show = new JTextArea(24, 80);
            show.setTabSize(2);
            JScrollPane scrollPane2 = new JScrollPane(show);
            scrollPane2
                    .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            scrollPane2.setPreferredSize(show
                    .getPreferredScrollableViewportSize());
            // scrollPane2.setMinimumSize(show.getMinimumSize() );
            panel.add(scrollPane2, c);
            add(panel);

            c.weighty = 0.0;
            c.weightx = 0.0;
            c.fill = GridBagConstraints.NONE;
            c.anchor = GridBagConstraints.CENTER;
            c.gridheight = GridBagConstraints.REMAINDER;
            c.gridwidth = GridBagConstraints.REMAINDER;
            closeButton = new JButton("Close");
            closeButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    setVisible(false);
                }
            });
            panel.add(closeButton, c);

            this.setSize(panel.getSize());
            this.pack();
        }

        private void displaySchema() {
            try {
                String typeName = (String) typeNameSelect.getSelectedItem();
                FeatureType schema = dataStore.getSchema(typeName);
                display(schema);
            } catch (Throwable t) {
                display(t);
            }
        }

        protected void display(Filter filter) throws Exception {
            FilterTransformer transform = new FilterTransformer();
            transform.setIndentation(2);
            String xml = transform.transform(filter);

            show.setText(xml);
        }

        public void display(FeatureType schema) {
            if (schema == null) {
                show.setText("");
                return;
            }
            StringBuffer buf = new StringBuffer();
            buf.append("typeName=");
            buf.append(schema.getTypeName());

            buf.append(" namespace=");
            buf.append(schema.getNamespace());
            buf.append("attributes = ([\n");

            for (int index = 0; index < schema.getAttributeCount(); index++) {
                AttributeType type = schema.getAttributeType(index);
                buf.append(type.getLocalName());
                buf.append(" [\n");

                buf.append("\t binding=");
                buf.append(type.getBinding());
                buf.append("\n");

                buf.append("\t minOccurs=");
                buf.append(type.getMinOccurs());
                buf.append(" maxOccurs=");
                buf.append(type.getMaxOccurs());
                buf.append(" nillable=");
                buf.append(type.isNillable());
                buf.append("\n");

                buf.append("\t restrictions=");
                buf.append(type.getRestriction());
                buf.append("\n");

                if (type instanceof GeometryAttributeType) {
                    GeometryAttributeType geomType = (GeometryAttributeType) type;
                    buf.append("\t crs=");
                    if (geomType.getCoordinateSystem() == null) {
                        buf.append("null");
                    } else {
                        buf.append(geomType.getCoordinateSystem().getName());
                    }
                    buf.append("\n");
                }
                buf.append("]\n");
            }
            buf.append(")");
            show.setText(buf.toString());
        }

        public FeatureCollection filter(String text) throws Exception {
            Filter filter;
            filter = CQL.toFilter(text);

            String typeName = (String) typeNameSelect.getSelectedItem();
            DefaultQuery query = new DefaultQuery();
            query.setTypeName(typeName);
            query.setFilter(filter);
            query.setMaxFeatures(1000);

            FeatureSource table = dataStore.getFeatureSource(typeName);
            return table.getFeatures(query);
        }

        protected void display(FeatureCollection features) throws Exception {
            if (features == null) {
                show.setText("empty");
                return;
            }
            final FeatureType schema = features.getSchema();
            final StringBuffer buf = new StringBuffer();

            buf.append(DataUtilities.spec(schema));
            buf.append("\n");

            features.accepts(new FeatureVisitor() {
                public void visit(Feature feature) {
                    buf.append(feature.getID());
                    buf.append(" [\n");
                    for (int index = 0; index < schema.getAttributeCount(); index++) {
                        AttributeType type = schema.getAttributeType(index);
                        String name = type.getLocalName();
                        buf.append("\t");
                        buf.append(name);
                        buf.append("=");
                        buf.append(feature.getAttribute(name));
                    }
                    buf.append("]");
                }
            }, null);
            show.setText(buf.toString());
        }

        public void display(Throwable t) {
            show.setText(t.getLocalizedMessage());
            show.setForeground(Color.RED);
        }

        private void selectFeatures() {
            try {
                String text = query.getText();
                FeatureCollection features = filter(text);
                display(features);
            } catch (Throwable t) {
                display(t);
            }
        }

        private void cqlToFilter() {
            try {
                String text = query.getText();
                Filter filter = CQL.toFilter(text);

                display(filter);
            } catch (Throwable t) {
                display(t);
            }
        }
    }
}