package usda.weru.gis;

import java.awt.Color;

import java.io.FileNotFoundException;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import java.io.FileFilter;

import org.geotools.data.FeatureSource;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.map.MapContext;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Fill;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.SLD;
import org.geotools.styling.SLDParser;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.Symbolizer;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.FilterFactory;

import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import de.schlichtherle.io.File;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;

import org.geotools.data.DefaultQuery;
import org.geotools.data.Query;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.map.DefaultMapLayer;
import org.geotools.map.MapLayer;
import org.geotools.styling.PolygonSymbolizer;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import usda.weru.util.ConfigData;
import usda.weru.util.Util;

public class MapData {

    private static Logger LOGGER = Logger.getLogger(MapData.class);
    StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(null);
    FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null);

    /**
     * Prompt the user for a file and open up ImageLab.
     *
     * @param args
     *                filename of image
     * @throws Exception 
     */
    public static void prepareGISData(GISData data) throws Exception {

        File gisDir = new File(ConfigData.getDefault().getDataParsed(ConfigData.GISData));

        if (!gisDir.exists()) {
            LOGGER.warn("GIS data directory does not exist: " + gisDir.getAbsolutePath());
            return;
        }


        File[] shapefiles = Util.findFiles(gisDir, new FileFilter() {

            @Override
            public boolean accept(java.io.File f) {
                return f.getName().toLowerCase().endsWith(".shp");
            }
        }, true, -1);
        
        
        for(File shapefile : shapefiles){

            int pos = 1000;

            String name = shapefile.getName().toLowerCase();

            if(name.contains("world")){
                pos = 0;
                name = "world";
            }
            else if (name.startsWith("st")){
                pos = 10;
                name = "us_states";
            }
            else if (name.startsWith("co")){
                pos = 20;
                name = "us_counties";
            }
            else if (name.startsWith("cmz")){
                pos = 30;
                name = "cmz";
            }

            else if (name.contains("cligen_boundary")){
                pos = 51;
                //name = "cligen boundary";
            }
            else if (name.startsWith("cligen_")){
                pos = 50;
                //name = "cligen area";
            }

            else if (name.startsWith("windgen_")){
                pos = 60;
                //name = "windgen area";
            }

            if(name.endsWith(".shp")){
                name = name.substring(0, name.length() - 3);
            }

            createDatastore(data, name, shapefile.getAbsolutePath(), pos);
        }





    }

    public static boolean isLayerVisibleDefault(Name name) {
        if ("world".equals(name.toString())) {
            return true;
        } else if ("us_states".equals(name.toString())) {
            return true;
        } else if ("us_counties".equals(name.toString())) {
            return true;
        } else {
            return false;
        }
    }

    private static void createDatastore(GISData data, String name, String path, int pos) throws Exception {
        try {
            if (path == null) {
                return;
            }
            File file = new File(path);
            if (!file.exists()) {
                return;
            }
            URL url = file.toURI().toURL();
            Map params = new HashMap();
            params.put("url", url);
            //TODO: activate indexing
            DataStore store = DataStoreFinder.getDataStore(params);

            data.register(name, store, pos);

        } catch (Exception e) {
            LOGGER.error("Unable to read shapefile: " + path, e);
        }
    }

    public MapLayer createLayer(FeatureSource source) {
        FeatureType schema = source.getSchema();

        URI sourceURI = source.getDataStore().getInfo().getSource();
        File file = new File(sourceURI);
        Style style = createStyle(file, schema);
        fixup(style);

        MapLayer layer = new DefaultMapLayer(source, style);
        layer.setTitle(file.getName());

//        if (file.getName().toLowerCase().contains("cligen") || file.getName().toLowerCase().contains("windgen")) {
//            //hack in a filter ro prevent the int/out polygons from showing
//            try {
//                Filter filter = CQL.toFilter("NAME NOT LIKE 'out%' AND NAME NOT LIKE 'int%'");
//                Query query = new DefaultQuery(null, filter);
//                layer.setQuery(query);
//
//            } catch (CQLException ce) {
//                ce.printStackTrace();
//            }
//
//
//        }

        return layer;

    }

    private void fixup(Style style) {
    }

    private Style createStyle(File file, FeatureType schema) {
        File sld = toSLDFile(file);
        if (sld.exists()) {
            return createFromSLD(sld);
        }
        Class type = schema.getGeometryDescriptor().getType().getBinding();
        if (type.isAssignableFrom(Polygon.class) || type.isAssignableFrom(MultiPolygon.class)) {
            return createPolygonStyle();
        } else if (type.isAssignableFrom(LineString.class) || type.isAssignableFrom(MultiLineString.class)) {
            return createLineStyle();
        } else {
            return createPointStyle();
        }
    }

    private Style createFromSLD(File sldFile) {
        SLDParser stylereader;
        try {
            stylereader = new SLDParser(styleFactory, sldFile);
            Style[] style = stylereader.readXML();
            return style[0];
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, e.getMessage());
            e.printStackTrace();
            System.exit(0);

        }
        return null;
    }

    private Style createPointStyle() {
        Style style;
        PointSymbolizer symbolizer = styleFactory.createPointSymbolizer();
        symbolizer.getGraphic().setSize(filterFactory.literal(1));
        Rule rule = styleFactory.createRule();
        rule.setSymbolizers(new Symbolizer[]{symbolizer});
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();
        fts.setRules(new Rule[]{rule});
        style = styleFactory.createStyle();
        style.addFeatureTypeStyle(fts);
        return style;
    }

    private Style createLineStyle() {
        Style style;

        LineSymbolizer symbolizer = styleFactory.createLineSymbolizer();
        SLD.setLineColour(symbolizer, Color.BLUE);
        symbolizer.getStroke().setWidth(filterFactory.literal(1));
        symbolizer.getStroke().setColor(filterFactory.literal(Color.BLUE));

        Rule rule = styleFactory.createRule();
        rule.setSymbolizers(new Symbolizer[]{symbolizer});
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();
        fts.setRules(new Rule[]{rule});
        style = styleFactory.createStyle();
        style.addFeatureTypeStyle(fts);
        return style;
    }

    private Style createPolygonStyle() {
        Style style;
        PolygonSymbolizer symbolizer = styleFactory.createPolygonSymbolizer();
        Fill fill = styleFactory.createFill(filterFactory.literal("#FFAA00"), filterFactory.literal(0.5));

        symbolizer.setFill(fill);
        Rule rule = styleFactory.createRule();
        rule.setSymbolizers(new Symbolizer[]{symbolizer});
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();
        fts.setRules(new Rule[]{rule});
        style = styleFactory.createStyle();
        style.addFeatureTypeStyle(fts);
        return style;
    }



    /** Figure out the URL for the "sld" file */
    public File toSLDFile(File file) {
        String filename = file.getAbsolutePath();
        if (filename.endsWith(".shp") || filename.endsWith(".dbf") || filename.endsWith(".shx")) {
            filename = filename.substring(0, filename.length() - 4);
            filename += ".sld";
        } else if (filename.endsWith(".SLD") || filename.endsWith(".SLD") || filename.endsWith(".SLD")) {
            filename = filename.substring(0, filename.length() - 4);
            filename += ".SLD";
        }
        return new File(filename);
    }
}
