package org.geotools.demo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;

import org.geotools.data.DataStoreFactorySpi;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.feature.AttributeType;
import org.geotools.feature.AttributeTypeFactory;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.FeatureType;
import org.geotools.feature.FeatureTypeBuilder;
import org.geotools.feature.SchemaException;
import org.geotools.referencing.crs.DefaultGeographicCRS;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;

public class Csv2Shp {

	/**
	 * This example takes a CSV file and produces a shape file.
	 * <p>
	 * The interesting part of this example is the use of a Factory when
	 * creating objects.
	 * 
	 * <pre><code>
	 * GeometryFactory factory = new GeometryFactory();
	 * Point point = factory.createPoint(new Coordinate(longitude, latitude));
	 * </code></pre>
	 * 
	 * These two classes come from the JTS Topology Suite responsible for the
	 * "rocket science" aspect of GIS - determining the relationships between
	 * geometry shapes.
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		File file = getCSVFile(args);
		FeatureType type = createFeatureType();

		FeatureCollection collection = FeatureCollections.newCollection();
		BufferedReader reader = new BufferedReader(new FileReader(file));
		GeometryFactory geometryFactory = new GeometryFactory();
		try {
			String line = reader.readLine();
			System.out.println("Header: " + line);
			for (line = reader.readLine(); line != null; line = reader
					.readLine()) {
				String split[] = line.split("\\,");
				double longitude = Double.parseDouble(split[0]);
				double latitude = Double.parseDouble(split[1]);
				String name = split[2];

				Coordinate coordinate = new Coordinate(longitude, latitude);
				Point point = geometryFactory.createPoint(coordinate);
				Feature feature = type.create(new Object[] { point, name });

				collection.add(feature);
			}
		} finally {
			reader.close();
		}
		File newFile = getNewShapeFile(file);

		DataStoreFactorySpi factory = new ShapefileDataStoreFactory();

		Map create = new HashMap();
		create.put("url", newFile.toURI().toURL());
		create.put("create spatial index", Boolean.TRUE);

		ShapefileDataStore newDataStore = (ShapefileDataStore) factory
				.createNewDataStore(create);
		newDataStore.createSchema(type);
		newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

		Transaction transaction = new DefaultTransaction("create");
		String typeName = newDataStore.getTypeNames()[0];
		FeatureStore featureStore = (FeatureStore) 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);
	}

	/**
	 * This this method we Create a "Location" feature type consisting
	 * of a location (ie a Point) and a name (ie a String).
	 * <p>
	 * This implementation uses the DataUtilities class as a nice readable short-cut.
	 * There are several problems with this result:
	 * <ul>
	 * <li>The name "Location" is unqualified (only important if you are are working
	 * with lots of conflicting GML files)
	 * <li>The "location" Point does not have a CoordinateReferenceSystem associated with
	 * it - this limits what you can do with the data (no reprojection or drawing)
	 * <li>The "name" String does not have a fixed length (so when we create a shapefile
	 * it will default to 255 characters (a really big DBF file)
	 * <ul>
	 * Please look at AttributeTypeFactory and FeatureTypeBuilder to see how to resolve
	 * these issues.
	 * 
	 * @return Location FeatureType
	 * @throws SchemaException 
	 */
	private static FeatureType createFeatureType() throws SchemaException {
		FeatureType schema = DataUtilities.createType("Landmarks","location:Point,name:String");
		return schema;
	}
	/**
	 * This method uses AttributeTypeFactory and FeatureTypeBuilder to
	 * create the "Location" FeatureType we are going to export.
	 * 
	 * @return Location FeatureType
	 * @throws Exception
	 */
	public FeatureType createFeatureType2() throws Exception {
		final AttributeType LOCATION = AttributeTypeFactory.newAttributeType(
				"Location",
				Point.class,
				false,
				0,
				null,
				DefaultGeographicCRS.WGS84); 		
		final AttributeType NAME = AttributeTypeFactory.newAttributeType(
				"Name",        // name of attirbute type
				String.class,  // attribute type binding
				true,          // nillable
				15             // 15 characters allowed
		);
		
		FeatureTypeBuilder builder = FeatureTypeBuilder.newInstance("Landmarks");
		builder.addType( LOCATION );
		builder.addType( NAME );
		
		FeatureType schema = builder.getFeatureType();		
		return schema;
	}
	/**
	 * This method takes the original csv file name as a reference point
	 * and prompts the user for a new filename.
	 * @param origional The origional csv file
	 * @return filename provided by the user
	 */
	private static File getNewShapeFile(File origional) {
		String path = origional.getAbsolutePath();
		String newPath = path.substring(0, path.length() - 4) + ".shp";

		JFileChooser chooser = new JFileChooser();
		chooser.setDialogTitle("Save 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();
		return newFile;
	}

	private static File getCSVFile(String[] args) throws FileNotFoundException {
		File file;
		if (args.length == 0) {
			JFileChooser chooser = new JFileChooser();
			chooser.setDialogTitle("Open CSV file");
			chooser.setFileFilter(new FileFilter() {
				public boolean accept(File f) {
					return f.isDirectory() || f.getPath().endsWith("csv")
							|| f.getPath().endsWith("CSV");
				}

				public String getDescription() {
					return "Comma Seperated Value";
				}
			});
			int returnVal = chooser.showOpenDialog(null);

			if (returnVal != JFileChooser.APPROVE_OPTION) {
				System.exit(0);
			}
			file = chooser.getSelectedFile();

			System.out.println("Opening CVS file: " + file.getName());
		} else {
			file = new File(args[0]);
		}
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
		return file;
	}

}