import java.awt.BorderLayout;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import org.geotools.caching.featurecache.FeatureCache;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultFeatureResults;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFactorySpi;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.indexed.IndexedShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.type.*;

import org.geotools.factory.Hints;
import org.geotools.feature.*;

import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureImpl;



import org.geotools.filter.text.cql2.CQL;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.graph.util.SimpleFileFilter;
import org.geotools.map.DefaultMapContext;
import org.geotools.map.MapContext;
import org.geotools.metadata.iso.citation.Citations;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.factory.PropertyAuthorityFactory;
import org.geotools.referencing.factory.ReferencingFactoryContainer;
import org.geotools.swing.JMapFrame;
import org.geotools.swing.data.JFileDataStoreChooser;


import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.identity.FeatureId;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

import fr.ocelet.runtime.List;
import fr.ocelet.runtime.Group;

public class ShapeFile {

	private File file;
	private List<LineString> lineStrings = new List<LineString>();
	private List<Polygon> polygons = new List<Polygon>();
	private List<Point> points = new List<Point>();
	private FeatureSource source;
	
	public ShapeFile(){
		//readPropertyFile();
	}
	public void read(String fileName){
		
		file = new File(fileName);
		URL url;
		try {
			url = file.toURI().toURL();
			 ShapefileDataStore store = new ShapefileDataStore(url);
			
		     String name = store.getTypeNames()[0];
		     
		     try {
				this.source = store.getFeatureSource(name);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	

	    
	}
	
	public List<List<List<Double>>> getCoordinates(){
		
		List<List<List<Double>>> coordsList = new List<List<List<Double>>>();
		 FeatureCollection featureCollection = null;
		try {
			featureCollection = source.getFeatures();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	    
	    for(Object o : featureCollection.toArray()){
	    	SimpleFeatureImpl sfi = (SimpleFeatureImpl)o;
	    	List<List<Double>> coords = new List<List<Double>>();
	    	if(sfi.getDefaultGeometry() instanceof MultiLineString){
		    	MultiLineString ms = (MultiLineString)sfi.getDefaultGeometry();
		    	
		    	Coordinate[] coordinates = ms.getCoordinates();
		    	for(Coordinate coord : coordinates){
	 				List<Double> list = new List<Double>();
	 				list.add(coord.x);
	 				list.add(coord.y);
	 				coords.add(list);
	 				
	 			}
	    	}
	    	/*if(sfi.getDefaultGeometry() instanceof MultiPolygon){
	    		MultiPolygon mp = (MultiPolygon)sfi.getDefaultGeometry();
		    	
		    	Coordinate[] coordinates = mp.getCoordinates();
		    	for(Coordinate coord : coordinates){
	 				List<Double> list = new List<Double>();
	 				list.add(coord.x);
	 				list.add(coord.y);
	 				coords.add(list);
	 				
	 			}
	    	}
	    	if(sfi.getDefaultGeometry() instanceof MultiPoint){
	    		MultiPoint mp = (MultiPoint)sfi.getDefaultGeometry();
		    	
		    	Coordinate[] coordinates = mp.getCoordinates();
		    	for(Coordinate coord : coordinates){
	 				List<Double> list = new List<Double>();
	 				list.add(coord.x);
	 				list.add(coord.y);
	 				coords.add(list);
	 				
	 			}
	    	}*/
	    	coordsList.add(coords);
	    	
	    }
	     	
	     	return coordsList;
	}
	
	public boolean touches(List<List<Double>> list1,List<List<Double>> list2){
		
		for(List<Double> l1 : list1){			
			for(List<Double> l2 : list2){
				if(l1.get(0) == l2.get(0) && l1.get(1) == l2.get(1)){
					return true;
				}				
			}
		}
		return false;
	}
	
	

	
	public void view(String fileName){
		
		  File file = new File(fileName);//JFileDataStoreChooser.showOpenFile(fileName, null);
	        if (file == null) {
	            return;
	        }

	        FileDataStore store = null;
			try {
				store = FileDataStoreFinder.getDataStore(file);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        FeatureSource featureSource = null;
			try {
				featureSource = store.getFeatureSource();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

	        // Create a map context and add our shapefile to it
	        MapContext map = new DefaultMapContext();
	        map.setTitle("Shape file viewer");
	        map.addLayer(featureSource, null);

	     /*   Viewer v = new Viewer();
	        v.addMap(map);
	        v.view();*/
	        // Now display the map
	        JMapFrame.showMap(map);

	}
	
	public void readPropertyFile(){
		
		
	/*	URL url = // application directory or user directory? where you are looking for epsg.properties
			 new URL(url, "epsg.properties"); //$NON-NLS-1$
			if ("file".equals(proposed.getProtocol())) { //$NON-NLS-1$
			    File file = new File(proposed.toURI());
			    if (file.exists()) {
			        epsg = file.toURI().toURL();
			    }
			}*/
			File file = new File("epsg.properties");
			// you may try several locations...
			URL epsg = null;
			try {
				epsg = file.toURI().toURL();
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (epsg != null) {
			    Hints hints = new Hints(Hints.CRS_AUTHORITY_FACTORY, PropertyAuthorityFactory.class);
			    ReferencingFactoryContainer referencingFactoryContainer = ReferencingFactoryContainer
			                        .instance(hints);

			    PropertyAuthorityFactory factory = null;
				try {
					factory = new PropertyAuthorityFactory(
					                    referencingFactoryContainer, Citations.fromName("EPSG"), epsg);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} //$NON-NLS-1$

			    ReferencingFactoryFinder.addAuthorityFactory(factory);
			    ReferencingFactoryFinder.scanForPlugins(); // hook everything up
			}

	}
	
	public void createLineString(List<List<Double>> coordinatesList){
		
		Coordinate[] coordinates = new Coordinate[coordinatesList.size()];
		
		int i=0;
		for(List<Double> list : coordinatesList){
			Coordinate coordinate = new Coordinate();
			coordinate.x = list.get(0);
			coordinate.y = list.get(1);
			coordinates[i] = coordinate;
			i++;
		}
		
		GeometryFactory geometryFactory = new GeometryFactory();
		LineString lineString = geometryFactory.createLineString(coordinates);
	    lineStrings.add(lineString);
	}
	
public void createPolygon(List<List<Double>> coordinatesList,List<List<List<Double>>> holes){
		
		GeometryFactory geometryFactory = new GeometryFactory();
		Coordinate[] coordinates = new Coordinate[coordinatesList.size()-1];
		
		int i=0;
		for(List<Double> list : coordinatesList){
			Coordinate coordinate = new Coordinate();
			coordinate.x = list.get(0);
			coordinate.y = list.get(1);
			coordinates[i] = coordinate;
			i++;
		}
		LinearRing linearRingCoords = geometryFactory.createLinearRing(coordinates);
		
		int j =0;
		LinearRing[] linearRingHoles = new LinearRing[holes.size()-1];
		for(List<List<Double>> hole : holes){
			Coordinate[] holesCoordinates = new Coordinate[hole.size()-1];
			
			for(List<Double> list : hole){
				Coordinate coordinate = new Coordinate();
				coordinate.x = list.get(0);
				coordinate.y = list.get(1);
				holesCoordinates[j] = coordinate;
				
			}
			LinearRing linearRingHole = geometryFactory.createLinearRing(holesCoordinates);
			linearRingHoles[j] = linearRingHole;
			j++;
		}			
		
		Polygon polygon = geometryFactory.createPolygon(linearRingCoords, linearRingHoles);
		polygons.add(polygon);
	   
	}

public void createPoint(List<List<Double>> coordinatesList){
	
	GeometryFactory geometryFactory = new GeometryFactory();
	
	
	int i=0;
	for(List<Double> list : coordinatesList){
		Coordinate coordinate = new Coordinate();
		coordinate.x = list.get(0);
		coordinate.y = list.get(1);
		Point point = geometryFactory.createPoint(coordinate);
		points.add(point);
	}
	
	
	
    
}
public void createShapeFile(String fileName){
	
	
	FileDataStoreFactorySpi factory = new IndexedShapefileDataStoreFactory();

	File file = new File(fileName+".shp");
	Map map = null;
	try {
		map = Collections.singletonMap( "url", file.toURI().toURL() );
	} catch (MalformedURLException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	DataStore myData = null;
	try {
		myData = factory.createNewDataStore( map );
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	SimpleFeatureType featureType = null;
	try {
		featureType = DataUtilities.createType( fileName, "geom:Point" );
	} catch (SchemaException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	try {
		myData.createSchema( featureType );
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
}

public void createShapeFile(String fileName,String fileSpec){
	
	
	FileDataStoreFactorySpi factory = new IndexedShapefileDataStoreFactory();

	File file = new File(fileName+".shp");
	Map map = null;
	try {
		map = Collections.singletonMap( "url", file.toURI().toURL() );
	} catch (MalformedURLException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	DataStore myData = null;
	try {
		myData = factory.createNewDataStore( map );
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	SimpleFeatureType featureType = null;
	try {
		featureType = DataUtilities.createType( fileName, fileSpec );
	} catch (SchemaException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	try {
		myData.createSchema( featureType );
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	
}

public MapContext getMap(FeatureSource featureSource){	
	
	MapContext map = new DefaultMapContext();
    map.setTitle("Shape file viewer");
    map.addLayer(featureSource, null);
    
    return map;
    
}

 public class Viewer extends JFrame{
	 
	 
	/**
	 * 
	 */
	private static final long serialVersionUID = 3380739697935242341L;
	
	JTabbedPane jTabbedPane = new JTabbedPane();
	 
	 public Viewer(){
		 super();
		 init();
	 }
	 public void init(){
		
		 setLayout(new BorderLayout());
		 this.setSize(800, 600);
		 jTabbedPane.setSize(600, 500);
		 add(jTabbedPane, BorderLayout.NORTH);
	 }
	 
	 public void addMap(MapContext map){
		 
		 JPanel jpanel = new JPanel();
		 
		 JMapFrame mapFrame = new JMapFrame();		
		 
		 mapFrame.showMap(map);
		 
	 }
	 
	 public void view(){
		 this.setVisible(true);
	 }
 }

	public void run(){
		Viewer v = new Viewer();
		v.setVisible(true);
	}
	
	/*public void addMap(LineString lineString){
		
		
		   Double area = 0.0;
		   Double perimeter = 0.0;
      
		FeatureType featureType = DataUtilities.createType( "name", "geom:Point,name:String,coordinate:Double,description:String" );
		  
		      SimpleFeature f = ((URI) featureType).create(new Object[] {lineString, area, perimeter});
              FeatureReader newFeatureReader = DataUtilities.reader(new SimpleFeature[] {f});
              newFeatureStore.addFeatures(newFeatureReader);

	}*/
	
	public void writeToFile(List<List<Double>> list,String fileName){
		
		
	}

	public void addFeature(List<List<Double>> list,String fileName){
		
		Coordinate[] coordinates = new Coordinate[list.size()];
		for(List<Double> l : list){
			Coordinate coord = new Coordinate(l.get(0),l.get(1));
			coordinates[coordinates.length-1] = coord;
		}
		//SimpleFeatureType feat = new Si
		SimpleFeatureType featureType = null;
		try {
			featureType = DataUtilities.createType( fileName, "geom:LineString" );
		} catch (SchemaException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 SimpleFeatureCollection collection = FeatureCollections.newCollection();
		 GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
	     SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
	        
	     LineString ls = geometryFactory.createLineString(coordinates);
	     
	     featureBuilder.add(ls);
       //  featureBuilder.add(name);
        // featureBuilder.add(number);
         SimpleFeature feature = featureBuilder.buildFeature(null);
         collection.add(feature);

	}
	
	public void transaction(DataStore newDataStore,SimpleFeatureCollection collection){
		
		   Transaction transaction = new DefaultTransaction("create");

	         String typeName = null;
			try {
				typeName = newDataStore.getTypeNames()[0];
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	         
	         SimpleFeatureSource featureSource = null;
			try {
				featureSource = newDataStore.getFeatureSource(typeName);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

	         if (featureSource instanceof SimpleFeatureStore) {
	             SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

	             featureStore.setTransaction(transaction);
	             try {
	                 featureStore.addFeatures(collection);
	                 transaction.commit();

	             } catch (Exception problem) {
	                 problem.printStackTrace();
	                 try {
						transaction.rollback();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

	             } finally {
	                 try {
						transaction.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
	             }
	             System.exit(0); // success!
	         } else {
	             System.out.println(typeName + " does not support read/write access");
	             System.exit(1);
	         }
	}
	
	public void reset(){
		
		lineStrings = new List<LineString>();
		polygons = new List<Polygon>();
		points = new List<Point>();
	}
}
