content: /**
   * ﻿Copyright (C) 2007 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * As an exception to the terms of the GPL, you may copy, modify,
   * propagate, and distribute a work formed by combining 52°North WPS
   * GeoTools Modules with the Eclipse Libraries, or a work derivative of
   * such a combination, even if such copying, modification, propagation, or
   * distribution would otherwise violate the terms of the GPL. Nothing in
   * this exception exempts you from complying with the GPL in all respects
   * for all of the code used other than the Eclipse Libraries. You may
   * include this exception and its grant of permissions when you distribute
   * 52°North WPS GeoTools Modules. Inclusion of this notice with such a
   * distribution constitutes a grant of such permissions. If you do not wish
   * to grant these permissions, remove this paragraph from your
   * distribution. "52°North WPS GeoTools Modules" means the 52°North WPS
   * modules using GeoTools functionality - software licensed under version 2
   * or any later version of the GPL, or a work based on such software and
   * licensed under the GPL. "Eclipse Libraries" means Eclipse Modeling
   * Framework Project and XML Schema Definition software distributed by the
   * Eclipse Foundation and licensed under the Eclipse Public License Version
   * 1.0 ("EPL"), or a work based on such software and licensed under the EPL.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
   * Public License for more details.
   */
  package org.n52.wps.io;
  
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.math.BigInteger;
  import java.net.URLDecoder;
  import java.util.Collection;
  
  import javax.xml.namespace.QName;
  
  import org.geotools.feature.NameImpl;
  import org.geotools.feature.simple.SimpleFeatureBuilder;
  import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
  import org.geotools.gml2.SrsSyntax;
  import org.geotools.referencing.CRS;
  import org.n52.wps.commons.WPSConfig;
  import org.opengis.feature.Feature;
  import org.opengis.feature.Property;
  import org.opengis.feature.simple.SimpleFeature;
  import org.opengis.feature.simple.SimpleFeatureType;
  import org.opengis.feature.type.GeometryDescriptor;
  import org.opengis.feature.type.Name;
  import org.opengis.feature.type.PropertyDescriptor;
  import org.opengis.geometry.aggregate.MultiCurve;
  import org.opengis.geometry.aggregate.MultiSurface;
  import org.opengis.geometry.primitive.Curve;
  import org.opengis.referencing.crs.CoordinateReferenceSystem;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.vividsolutions.jts.geom.Envelope;
  import com.vividsolutions.jts.geom.Geometry;
  import com.vividsolutions.jts.geom.GeometryCollection;
  import com.vividsolutions.jts.geom.LineString;
  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;
  
  public class GTHelper {
  	private static Logger LOGGER = LoggerFactory.getLogger(GTHelper.class);
  	
  	public static SimpleFeatureType createFeatureType(Collection<Property> attributes, Geometry newGeometry, String uuid, CoordinateReferenceSystem coordinateReferenceSystem){
  		String namespace = "http://www.52north.org/"+uuid;
  		
  		SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
  		if(coordinateReferenceSystem==null){
  			coordinateReferenceSystem= getDefaultCRS();			
  		}
  		typeBuilder.setCRS(coordinateReferenceSystem);
  		typeBuilder.setNamespaceURI(namespace);
  		Name nameType = new NameImpl(namespace, "Feature-"+uuid);
  		typeBuilder.setName(nameType);
  		
  		for(Property property : attributes){
  			if(property.getValue()!=null){ 
  				String name = property.getName().getLocalPart();
  				Class<?> binding = property.getType().getBinding();
  				if(binding.equals(Envelope.class)){
  					continue;
  				}
  				if( 
  				   (binding.equals(Geometry.class) ||
  				    binding.equals(GeometryCollection.class) ||
  				   binding.equals(MultiCurve.class) || 
  				   binding.equals(MultiLineString.class) ||
  				   binding.equals(Curve.class) ||
  				   binding.equals(MultiPoint.class) ||
  				   binding.equals(MultiPolygon.class) ||
  				   binding.equals(MultiSurface.class) ||
  				   binding.equals(LineString.class) ||
  				   binding.equals(Point.class) ||
  				   binding.equals(LineString.class) ||
  				   binding.equals(Polygon.class)) 				  
  				 &&!name.equals("location")){
  									   
  					
  					if(newGeometry.getClass().equals(Point.class) && (!name.equals("location"))){
  						typeBuilder.add("GEOMETRY", MultiPoint.class);
  					}else if(newGeometry.getClass().equals(LineString.class) && (!name.equals("location"))){
  					
  						typeBuilder.add("GEOMETRY", MultiLineString.class);
  					}else if( newGeometry.getClass().equals(Polygon.class) && (!name.equals("location"))){
  					
  						typeBuilder.add("GEOMETRY", MultiPolygon.class);
  					}else if(!binding.equals(Object.class)){
  						typeBuilder.add("GEOMETRY", newGeometry.getClass());
  					}
  				}else{
  					if(!name.equals("location") && binding.equals(Object.class)){
  						try{
  							Geometry g = (Geometry)property.getValue();
  							if(g.getClass().equals(Point.class) && (!name.equals("location"))){
  								typeBuilder.add("GEOMETRY", MultiPoint.class);
  							}else if(g.getClass().equals(LineString.class) && (!name.equals("location"))){
  							
  								typeBuilder.add("GEOMETRY", MultiLineString.class);
  							}else if( g.getClass().equals(Polygon.class) && (!name.equals("location"))){
  							
  								typeBuilder.add("GEOMETRY", MultiPolygon.class);
  							}else{
  								typeBuilder.add("GEOMETRY", g.getClass());
  							}
  							
  						}catch(ClassCastException e){
  							
  						}
  						
  					}else if(!name.equals("location")){
  						typeBuilder.add(name, binding);
  					}
  				}
  			}
  		
  		 
  		}
  		
  		SimpleFeatureType featureType;
  		
  		featureType = typeBuilder.buildFeatureType();
  		return featureType;
  	}
  	
  	
  	
  	public static SimpleFeatureType createFeatureType(Geometry newGeometry, String uuid, CoordinateReferenceSystem coordinateReferenceSystem){
  		String namespace = "http://www.52north.org/"+uuid;
  		
  		SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
  		if(coordinateReferenceSystem==null){
  			coordinateReferenceSystem= getDefaultCRS();
  		}
  		typeBuilder.setCRS(coordinateReferenceSystem);
  		typeBuilder.setNamespaceURI(namespace);
  		Name nameType = new NameImpl(namespace, "Feature-"+uuid);
  		typeBuilder.setName(nameType);		
  					
  		typeBuilder.add("GEOMETRY", newGeometry.getClass());
  					
  		SimpleFeatureType featureType;
  		
  		featureType = typeBuilder.buildFeatureType();
  		return featureType;
  	}
  	
  	public static SimpleFeature createFeature(String id, Geometry geometry, SimpleFeatureType featureType, Collection<Property> originalAttributes) {
  		
  			if(geometry==null || geometry.isEmpty()){
  				return null;
  			}
  			
  			SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
  			SimpleFeature feature = null;
  			Collection<PropertyDescriptor> featureTypeAttributes = featureType.getDescriptors();
  						
  			Object[] newData = new Object[featureType.getDescriptors().size()];
  			
  			int i = 0;
  			for(PropertyDescriptor propertyDescriptor : featureTypeAttributes){
  				for(Property originalProperty : originalAttributes){
  					if(propertyDescriptor.getName().getLocalPart().equals(originalProperty.getName().getLocalPart())){
  						if(propertyDescriptor instanceof GeometryDescriptor){
  							newData[i] = geometry;
  						}else{
  							newData[i] = originalProperty.getValue();
  						}
  					}
  				}
  				
  				if(propertyDescriptor instanceof GeometryDescriptor){
  					if(geometry.getGeometryType().equals("Point")){
  						Point[] points = new Point[1];
  						points[0] = (Point)geometry;
  						newData[i] = geometry.getFactory().createMultiPoint(points);
  					}else
  						if(geometry.getGeometryType().equals("LineString")){
  							LineString[] lineString = new LineString[1];
  							lineString[0] = (LineString)geometry;
  							newData[i] = geometry.getFactory().createMultiLineString(lineString);
  						}else
  							if(geometry.getGeometryType().equals("Polygon")){
  							Polygon[] polygons = new Polygon[1];
  							polygons[0] = (Polygon)geometry;
  							newData[i] = geometry.getFactory().createMultiPolygon(polygons);
  							}else{
  								newData[i] = geometry;
  							}
  					
  				}
  				i++;
  			}
  				
  		
  			
  			feature = featureBuilder.buildFeature(id, newData);
  		
  			return feature;
  	}
  	
  	public static Feature createFeature(String id, Geometry geometry, SimpleFeatureType featureType) {
  		
  		if(geometry==null || geometry.isEmpty()){
  			return null;
  		}
  		
  		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
  		SimpleFeature feature = null;
  					
  		Object[] newData = new Object[featureType.getDescriptors().size()];
  		
  		int i = 0;
  	
  		if(geometry.getGeometryType().equals("Point")){
  			Point[] points = new Point[1];
  			points[0] = (Point)geometry;
  			newData[i] = geometry.getFactory().createMultiPoint(points);
  		}else
  			if(geometry.getGeometryType().equals("LineString")){
  				LineString[] lineString = new LineString[1];
  				lineString[0] = (LineString)geometry;
  				newData[i] = geometry.getFactory().createMultiLineString(lineString);
  			}else
  				if(geometry.getGeometryType().equals("Polygon")){
  				Polygon[] polygons = new Polygon[1];
  				polygons[0] = (Polygon)geometry;
  				newData[i] = geometry.getFactory().createMultiPolygon(polygons);
  				}else{
  					newData[i] = geometry;
  				}
  			
  		
  		feature = featureBuilder.buildFeature(id, newData);
  	
  		return feature;
  }
  	
  		public static QName createGML3SchemaForFeatureType(SimpleFeatureType featureType){
  		
  		String uuid = featureType.getName().getNamespaceURI().replace("http://www.52north.org/", "");
  		String namespace = "http://www.52north.org/"+uuid;
  		String schema = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><xs:schema targetNamespace=\""+namespace+"\" " +
  				"xmlns:n52=\""+namespace+"\" "+
  				"xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "+
  				"xmlns:gml=\"http://www.opengis.net/gml\" "+
  				"elementFormDefault=\"qualified\" "+
  				"version=\"1.0\"> "+
  				"<xs:import namespace=\"http://www.opengis.net/gml\" "+
  				"schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/> ";
  				
  		String typeName = featureType.getGeometryDescriptor().getType().getBinding().getName();
  		String geometryTypeName = "";
  		if(typeName.contains("Point")){
  	    	geometryTypeName = "PointPropertyType";
  	    }
  	  	     
  		 if(typeName.contains("MultiPoint")){
  			 geometryTypeName = "MultiPointPropertyType";	
  					    }
  		 if(typeName.contains("LineString")){
  			 geometryTypeName = "CurvePropertyType";	
  		 }
  		 if(typeName.contains("MultiLineString")){
  			 geometryTypeName = "MultiCurvePropertyType";	
  		 }
  		 if(typeName.contains("Polygon")){
  			 geometryTypeName = "SurfacePropertyType";	
  		 }
  		 if(typeName.contains("MultiPolygon")){
  			 geometryTypeName = "MultiSurfacePropertyType";	
  		 }		
  				
  				// add feature type definition and generic geometry
  			schema = schema + "<xs:element name=\"Feature-"+uuid+"\" type=\"n52:FeatureType\" substitutionGroup=\"gml:_Feature\"/> " +
  					"<xs:complexType name=\"FeatureType\"> " +
  					"<xs:complexContent> " +
  					"<xs:extension base=\"gml:AbstractFeatureType\"> "+
  					"<xs:sequence> " +
  					//"<xs:element name=\"GEOMETRY\" type=\"gml:GeometryPropertyType\"> "+					
  					"<xs:element name=\"GEOMETRY\" type=\"gml:"+geometryTypeName+"\"> "+
  					"</xs:element> ";
  			
  			//add attributes
  			Collection<PropertyDescriptor> attributes = featureType.getDescriptors();
  			for(PropertyDescriptor property : attributes){
  				String attributeName = property.getName().getLocalPart();
  				if(!(property instanceof GeometryDescriptor)){
  					
  					if(property.getType().getBinding().equals(String.class) ){
  						schema = schema + "<xs:element name=\""+attributeName+"\" minOccurs=\"0\" maxOccurs=\"1\"> "+
  						"<xs:simpleType> ";
  						schema = schema + "<xs:restriction base=\"xs:string\"> "+
  						"</xs:restriction> "+
  						"</xs:simpleType> "+
  						"</xs:element> ";
  					}else if(property.getType().getBinding().equals(Integer.class)|| property.getType().getBinding().equals(BigInteger.class)){
  						schema = schema + "<xs:element name=\""+attributeName+"\" minOccurs=\"0\" maxOccurs=\"1\"> "+
  						"<xs:simpleType> ";
  						schema = schema + "<xs:restriction base=\"xs:integer\"> "+
  						"</xs:restriction> "+
  						"</xs:simpleType> "+
  						"</xs:element> ";
  					}else if(property.getType().getBinding().equals(Double.class)){
  						schema = schema + "<xs:element name=\""+attributeName+"\" minOccurs=\"0\" maxOccurs=\"1\"> "+
  						"<xs:simpleType> ";
  						schema = schema + "<xs:restriction base=\"xs:double\"> "+
  						"</xs:restriction> "+
  						"</xs:simpleType> "+
  						"</xs:element> ";
  					}
  				}
  			}		
  			
  			//close
  			schema = schema +  "</xs:sequence> "+
  		      "</xs:extension> "+
  		      "</xs:complexContent> "+
  		    "</xs:complexType> "+
  		  "</xs:schema>";
  			String schemalocation = "";
  			try {
  				schemalocation = storeSchema(schema, uuid);
  				
  			} catch (IOException e) {
  				LOGGER.error("Exception while storing schema.", e);
  				throw new RuntimeException("Exception while storing schema.", e);
  			}
  			return new QName(namespace, schemalocation);
  			
  		}
  		
  		public static QName createGML2SchemaForFeatureType(SimpleFeatureType featureType){
  			
  			String uuid = featureType.getName().getNamespaceURI().replace("http://www.52north.org/", "");
  			String namespace = "http://www.52north.org/"+uuid;
  			String schema = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><xs:schema targetNamespace=\""+namespace+"\" " +
  					"xmlns:n52=\""+namespace+"\" "+
  					"xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "+
  					"xmlns:gml=\"http://www.opengis.net/gml\" "+
  					"elementFormDefault=\"qualified\" "+
  					"version=\"1.0\"> "+
  					"<xs:import namespace=\"http://www.opengis.net/gml\" "+
  					"schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/> ";
  				
  					// add feature type definition and generic geometry
  				schema = schema + "<xs:element name=\"Feature\" type=\"n52:FeatureType\" substitutionGroup=\"gml:_Feature\"/> " +
  						"<xs:complexType name=\"FeatureType\"> " +
  						"<xs:complexContent> " +
  						"<xs:extension base=\"gml:AbstractFeatureType\"> "+
  						"<xs:sequence> " +
  						"<xs:element name=\"GEOMETRY\" type=\"gml:GeometryPropertyType\"> "+
  						"</xs:element> ";
  				
  				//add attributes
  				Collection<PropertyDescriptor> attributes = featureType.getDescriptors();
  				for(PropertyDescriptor property : attributes){
  					String attributeName = property.getName().getLocalPart();
  					if(!(property instanceof GeometryDescriptor)){
  						
  						if(property.getType().getBinding().equals(String.class) ){
  							schema = schema + "<xs:element name=\""+attributeName+"\" minOccurs=\"0\" maxOccurs=\"1\"> "+
  							"<xs:simpleType> ";
  							schema = schema + "<xs:restriction base=\"xs:string\"> "+
  							"</xs:restriction> "+
  							"</xs:simpleType> "+
  							"</xs:element> ";
  						}else if(property.getType().getBinding().equals(Integer.class)|| property.getType().getBinding().equals(BigInteger.class)){
  							schema = schema + "<xs:element name=\""+attributeName+"\" minOccurs=\"0\" maxOccurs=\"1\"> "+
  							"<xs:simpleType> ";
  							schema = schema + "<xs:restriction base=\"xs:integer\"> "+
  							"</xs:restriction> "+
  							"</xs:simpleType> "+
  							"</xs:element> ";
  						}else if(property.getType().getBinding().equals(Double.class)){
  							schema = schema + "<xs:element name=\""+attributeName+"\" minOccurs=\"0\" maxOccurs=\"1\"> "+
  							"<xs:simpleType> ";
  							schema = schema + "<xs:restriction base=\"xs:double\"> "+
  							"</xs:restriction> "+
  							"</xs:simpleType> "+
  							"</xs:element> ";
  						}
  					}
  				}		
  				
  				//close
  				schema = schema +  "</xs:sequence> "+
  			      "</xs:extension> "+
  			      "</xs:complexContent> "+
  			    "</xs:complexType> "+
  			  "</xs:schema>";
  				String schemalocation = "";
  				try {
  					schemalocation = storeSchema(schema, uuid);
  					
  				} catch (IOException e) {
  					LOGGER.error("Exception while storing schema.", e);
  					throw new RuntimeException("Exception while storing schema.", e);
  				}
  				return new QName(namespace, schemalocation);
  				
  			}
  
  		public static String storeSchema(String schema, String uuid) throws IOException {
  			
  			String domain = WPSConfig.class.getProtectionDomain().getCodeSource().getLocation().getFile();
  			
  			domain = URLDecoder.decode(domain, "UTF-8");
  			
  			int startIndex = domain.indexOf("WEB-INF");
  			if(startIndex<0){
  				//not running as webapp				
  				File f = File.createTempFile(uuid, ".xsd");
  				f.deleteOnExit();
  				FileWriter writer = new FileWriter(f);
  				writer.write(schema);
  				writer.flush();
  				writer.close();
  				return "file:"+f.getAbsolutePath();
  			}else{
  				domain = domain.substring(0,startIndex);			
  				String baseDirLocation = domain;
  			
  				String baseDir = baseDirLocation +  "schemas" + File.separator;
  				File folder = new File(baseDir);
  				if(!folder.exists()){
  					folder.mkdirs();
  				}
  				File f = new File(baseDir+uuid+".xsd");
  				FileWriter writer = new FileWriter(f);
  				writer.write(schema);
  				writer.flush();
  				writer.close();
  				
  				String url = WPSConfig.getServerBaseURL()+"/schemas/"+ uuid+".xsd";
  				return url;
  			}
  		}
  		
  		private static CoordinateReferenceSystem getDefaultCRS(){
  
  			try {
  				return CRS.decode("EPSG:4326");
  			} catch (Exception e) {
  				LOGGER.error("Exception while decoding CRS EPSG:4326", e);
  			}
  			return null;
  		}
  		
  		public static SrsSyntax getSrsSyntaxFromString(String syntaxString){		    
  		    return SrsSyntax.valueOf(syntaxString);		    
  		}
  
  }
  