package com.cognizant.clips.data.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFactorySpi;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultQuery;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.factory.GeoTools;
import org.geotools.feature.FeatureCollection;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

public class ShapeFileTransformer {
	private static HashMap<String, String> wktHashMap;

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		initializeHashMap();
		System.out.println("Welcome to GeoTools:" + GeoTools.getVersion());

		File file = promptShapeFile(args);

		Map<String, Object> connect = new HashMap<String, Object>();
		connect.put("url", file.toURI().toURL());

		DataStore dataStore = DataStoreFinder.getDataStore(connect);
		String[] typeNames = dataStore.getTypeNames();
		String typeName = typeNames[0];

		System.out.println("Reading content " + typeName);
		FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = dataStore
				.getFeatureSource(typeName);

		SimpleFeatureType simpleFeatureType = featureSource.getSchema();
		System.out.println("Header: " + DataUtilities.spec(simpleFeatureType));

		DefaultQuery query = new DefaultQuery();
		query.setTypeName(typeName);

		CoordinateReferenceSystem prj = simpleFeatureType
				.getCoordinateReferenceSystem();
		if (prj == null) {
			if (args.length < 1) {
				prj = getCoordinateReferenceSystem("No projection fround for "
						+ file + " please choose one:");

			} else {
				prj = getCoordinateReferenceSystem(args, "source");
			}
			query.setCoordinateSystem(prj);
		}
		CoordinateReferenceSystem crs = null;
		if (args.length < 2) {
			crs = getCoordinateReferenceSystem("Project " + file + " to:");
		} else {
			crs = getCoordinateReferenceSystem(args, "target");
		}
		query.setCoordinateSystemReproject(crs);

		FeatureCollection<SimpleFeatureType, SimpleFeature> collection = featureSource
				.getFeatures(query);
		File newFile = null;
		if (args.length < 3) {
			newFile = getNewShapeFile(file);
		} else {
			newFile = getNewShapeFile(args);
		}
		DataStoreFactorySpi factory = new ShapefileDataStoreFactory();
		Map<String, Serializable> create = new HashMap<String, Serializable>();
		create.put("url", newFile.toURI().toURL());
		create.put("create spatial index", Boolean.TRUE);
		DataStore newDataStore = factory.createNewDataStore(create);
		newDataStore.createSchema(collection.getSchema());
		Transaction transaction = new DefaultTransaction("Reproject");
		FeatureStore<SimpleFeatureType, SimpleFeature> featureStore;
		featureStore = (FeatureStore<SimpleFeatureType, SimpleFeature>) newDataStore
				.getFeatureSource(typeName);
		featureStore.setTransaction(transaction);
		try {
			featureStore.addFeatures(collection);
			transaction.commit();
		} catch (Exception problem) {
			problem.printStackTrace();
			transaction.rollback();
		} finally {
			transaction.close();
		}
		System.exit(0);
	}

	private static File getNewShapeFile(File file) {
		String path = file.getAbsolutePath();
		String newPath = path.substring(0, path.length() - 4) + "2.shp";

		JFileChooser chooser = new JFileChooser();
		chooser.setDialogTitle("Save reprojected shapefile");
		chooser.setSelectedFile(new File(newPath));
		chooser.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				return f.isDirectory() || f.getPath().endsWith("shp")
						|| f.getPath().endsWith("SHP");
			}

			public String getDescription() {
				return "Shapefiles";
			}
		});
		int returnVal = chooser.showSaveDialog(null);

		if (returnVal != JFileChooser.APPROVE_OPTION) {
			System.exit(0);
		}
		File newFile = chooser.getSelectedFile();
		if (newFile.equals(file)) {
			System.out.println("Cannot replace " + file);
			System.exit(0);
		}
		return newFile;
	}

	private static File getNewShapeFile(String[] args) {
		System.out.println(args[2]);
		return new File(args[2]);
	}

	private static CoordinateReferenceSystem getCoordinateReferenceSystem(
			String message) throws Exception {
		Set<String> codes = CRS.getSupportedCodes("EPSG");
		String selected = (String) JOptionPane.showInputDialog(null, message,
				"Choose a Projection", JOptionPane.QUESTION_MESSAGE, null,
				codes.toArray(), "4326");
		if (selected == null) {
			System.exit(0);
		}
		return CRS.decode("EPSG:" + selected);

	}

	private static CoordinateReferenceSystem getCoordinateReferenceSystem(
			String[] args, String sourceTarget) throws Exception {
		String wkt = "";
		String wktKey = "";
		if (sourceTarget.equals("source")) {
			wktKey = args[1];

		} else {
			wktKey = args[3];
		}
		wkt = wktHashMap.get(wktKey);
		return CRS.parseWKT(wkt);
	}

	private static File promptShapeFile(String[] args)
			throws FileNotFoundException {
		File file;
		if (args.length == 0) {
			JFileChooser chooser = new JFileChooser();
			chooser.setDialogTitle("Open Shapefile for Reprojection");
			chooser.setFileFilter(new FileFilter() {
				public boolean accept(File f) {
					return f.isDirectory() || f.getPath().endsWith("shp")
							|| f.getPath().endsWith("SHP");
				}

				public String getDescription() {
					return "Shapefiles";
				}
			});
			int returnVal = chooser.showOpenDialog(null);

			if (returnVal != JFileChooser.APPROVE_OPTION) {
				System.exit(0);
			}
			file = chooser.getSelectedFile();

			System.out
					.println("You chose to open this file: " + file.getName());
		} else {
			System.out.println(args[0]);
			file = new File(args[0]);
		}
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
		return file;
	}

	private static void initializeHashMap() {
		wktHashMap = new HashMap<String,String>();
		wktHashMap
				.put(
						"google",
						"GEOGCS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",SPHEROID[\"WGS 84\", 6378137.0, 298.257223563, AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\", 0.0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\", 0.017453292519943295],AXIS[\"Geodetic latitude\", NORTH],AXIS[\"Geodetic longitude\", EAST],AUTHORITY[\"EPSG\",\"4326\"]]");
		wktHashMap
				.put(
						"ndfd",
						"GEOGCS[\"NCEP_SPHERE\",DATUM[\"NCEP_SPHERE\",SPHEROID[\"NCEP_SPHERE\",6371200.0,0.0]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199432955]]");
		wktHashMap
				.put(
						"tiger",
						"GEOGCS[\"GCS_North_American_1983\",DATUM[\"D_North_American_1983\",SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]], AXIS[\"Latitude\",NORTH]]");
	}
}