package org.mbari.vdm;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

import javax.swing.JFrame;
import javax.swing.WindowConstants;

import org.geotools.data.coverage.grid.stream.StreamGridCoverageExchange;
import org.geotools.gui.swing.StyledMapPane;
import org.geotools.map.DefaultMapContext;
import org.geotools.map.MapContext;
import org.geotools.parameter.Parameter;
import org.geotools.referencing.FactoryFinder;
import org.geotools.renderer.j2d.RenderedMapScale;
import org.geotools.styling.ColorMap;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridCoverageExchange;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CRSFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.spatialschema.geometry.DirectPosition;

import ucar.nc2.NetcdfFileWriteable;

import com.domainlanguage.timeutil.Clock;
import com.domainlanguage.timeutil.SystemClock;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;

public class MapBuilder {

    ColorMap dataColorMap;
    ColorMap bathyColorMap;
    CoordinateReferenceSystem utm10;
    GridCoverage baseMap;
    public final StyleBuilder sb = new StyleBuilder();
    
    public JFrame createFrameToViewGrid(String gridName) throws Exception{
        GridCoverage gcDem = getBaseMap();
        
        ColorMap cm = getBathyColorMap();
        RasterSymbolizer rsDem = sb.createRasterSymbolizer(cm, 1);
        Style demStyle = sb.createStyle(rsDem);

        GridCoverage gcData = createGridCoverage(gridName);
        ColorMap cmData = getDataColorMap();

        RasterSymbolizer rsData = sb.createRasterSymbolizer(cmData, .5);
        Style dataStyle = sb.createStyle(rsData);

        // Build the map
        final MapContext map = new DefaultMapContext();
        
        map.addLayer(gcDem, demStyle);
//
        map.addLayer(gcData, dataStyle);

        // Show the map
        final StyledMapPane mapPane = new StyledMapPane();
        DirectPosition lowerCorner = gcDem.getEnvelope().getLowerCorner();
        DirectPosition upperCorner = gcDem.getEnvelope().getUpperCorner();
        Coordinate lowerCoord = new Coordinate(lowerCorner.getCoordinates()[0], lowerCorner.getCoordinates()[1]);
        Coordinate upperCoord = new Coordinate(upperCorner.getCoordinates()[0], upperCorner.getCoordinates()[1]);
        Envelope envelope = new Envelope(lowerCoord, upperCoord);
        map.setAreaOfInterest(envelope, gcDem.getCoordinateReferenceSystem());        
        mapPane.setMapContext(map);
        mapPane.getRenderer().addLayer(new RenderedMapScale());
        
        JFrame frame = new JFrame();
        frame.setTitle(gridName);
        frame.setContentPane(mapPane.createScrollPane());
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.setBounds(800, 200, 640, 480);
        frame.setVisible(true);
        return frame;
    }

    public ColorMap getDataColorMap() {
        if (dataColorMap == null) {
            Color transparent = new Color(0.0f, 0.0f, 0.0f, 0.0f);
            dataColorMap = sb.createColorMap(new String[]{"zero", "1-to-5",
                "5-to-10", "10-to-27", "28-to-infinity", "infinity"},
                new double[]{0, 1, 5, 10, 27, 100},
                new Color[]{transparent, Color.RED, Color.GREEN, Color.CYAN,
                    Color.PINK, transparent}, ColorMap.TYPE_INTERVALS);
        }
        return dataColorMap;
    }

    public GridCoverage getBaseMap() {
        if (baseMap == null) {
            try {
                baseMap = createGridCoverage("mont_super15.asc");
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        return baseMap;
    }
    

    public CoordinateReferenceSystem getUtm10CRS() {
        if (utm10 == null) {
            String wkt = "PROJCS[\"UTM_Zone_10N\", " + "GEOGCS[\"WGS84\", "
                + "DATUM[\"WGS84\", "
                + "SPHEROID[\"WGS84\", 6378137.0, 298.257223563]], "
                + "PRIMEM[\"Greenwich\", 0.0], "
                + "UNIT[\"degree\",0.017453292519943295], "
                + "AXIS[\"Longitude\",EAST], " + "AXIS[\"Latitude\",NORTH]], "
                + "PROJECTION[\"Transverse_Mercator\"], "
                + "PARAMETER[\"semi_major\", 6378137.0], "
                + "PARAMETER[\"semi_minor\", 6356752.314245179], "
                + "PARAMETER[\"central_meridian\", -123.0], "
                + "PARAMETER[\"latitude_of_origin\", 0.0], "
                + "PARAMETER[\"scale_factor\", 0.9996], "
                + "PARAMETER[\"false_easting\", 500000.0], "
                + "PARAMETER[\"false_northing\", 0.0], "
                + "UNIT[\"metre\",1.0], " + "AXIS[\"x\",EAST], "
                + "AXIS[\"y\",NORTH]]";

            CRSFactory csFactory = FactoryFinder.getCRSFactory(null);

            try {
                utm10 = csFactory.createFromWKT(wkt);
            } catch (FactoryException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        return utm10;
    }

    public ColorMap getBathyColorMap() {
        return getBlueBathyColorMap();
    }
    
    private ColorMap getRainbowBathyColorMap(){
        ColorMap cm = sb.createColorMap(new String[]{"fe", "fi", "fo", "crumb",
            "mum", "dumb", "stun", "above water", "above water"}, new double[]{-5000, -3000, -2800, -2500,
            -2000, -1000, 0, 1, 1000}, new Color[]{Color.BLUE.darker(), Color.BLUE,
            Color.CYAN, Color.GREEN, Color.YELLOW, Color.ORANGE, Color.RED, Color.BLACK, Color.BLACK},
            ColorMap.TYPE_RAMP);
        return cm;
    }
    
    private ColorMap getBlueBathyColorMap(){
        Color[] greys = new Color[11];
        int greyStart = 20;
        int greyEnd = 230;
        int currentGrey = greyStart;
        int step = (int)((greyEnd - greyStart)/(float)greys.length);
        for(int i = 0; i < greys.length; i++){
            greys[i] = new Color(currentGrey, currentGrey, currentGrey);
            currentGrey += step;
        }
        
        greys[greys.length -1] = Color.BLACK;
        greys[greys.length -2] = Color.BLACK;
                                  
        ColorMap cm = sb.createColorMap(new String[]{"fe", "fi", "fo", "crumb",
            "mum", "dumb", "stun", "foo", "bar", "above water", "above water"}, 
            new double[]{-5000, -3000, -2500,
            -2000, -1500, -1250, -1000, -500, 0, 1, 1000},
            greys,
            ColorMap.TYPE_RAMP);
        return cm;
    }
    
    public GridCoverage createGridCoverage(String gridName)
        throws IllegalArgumentException, IOException {
        if (gridName == null) {
            throw new IllegalArgumentException(
                "gridName argument can not be null");
        }
        URL url = getClass().getResource("/grids/" + gridName);
        if (url == null) {
            throw new FileNotFoundException("Could not find specified grid.");
        }
        GridCoverageExchange gce = new StreamGridCoverageExchange();

        GridCoverageReader reader = gce.getReader(url);

        Format format = reader.getFormat();

        // get the parameters and set them
        ParameterValueGroup paramDescriptor = format.getReadParameters();
        boolean isCompressed = gridName.endsWith("gz");
        paramDescriptor.parameter("Compressed").setValue(isCompressed);
        paramDescriptor.parameter("GRASS").setValue(false);
        format.getReadParameters().parameter("crs").setValue(getUtm10CRS());
        List paramList = paramDescriptor.values();
        GeneralParameterValue[] params = new GeneralParameterValue[paramList
            .size()];
        int i = 0;
        for (Iterator iter = paramList.iterator(); iter.hasNext(); i++) {
            Parameter param = (Parameter) iter.next();
            params[i] = param;
        }

        // read the grid
        System.out.println("Before grid coverage");
        long time = System.currentTimeMillis();
        GridCoverage gridCoverage = reader.read(params);
        System.out.println("time for grid coverage = " + (System.currentTimeMillis() - time));
        // just to be sure these big objects get garbage collected...
        reader = null;
        gce = null;
        format = null;
        return gridCoverage;
    }
}