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.datahandler.generator;
  
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.OutputStreamWriter;
  import java.io.Writer;
  import java.math.BigDecimal;
  import java.util.ArrayList;
  
  import net.opengis.examples.packet.DataType;
  import net.opengis.examples.packet.GMLPacketDocument;
  import net.opengis.examples.packet.GMLPacketType;
  import net.opengis.examples.packet.PropertyType;
  import net.opengis.examples.packet.StaticFeatureType;
  import net.opengis.examples.packet.PropertyType.Value;
  import net.opengis.gml.CoordType;
  import net.opengis.gml.LineStringPropertyType;
  import net.opengis.gml.LinearRingMemberType;
  import net.opengis.gml.LinearRingType;
  import net.opengis.gml.PointPropertyType;
  import net.opengis.gml.PolygonType;
  
  import org.geotools.feature.FeatureIterator;
  import org.n52.wps.io.data.IData;
  import org.n52.wps.io.data.binding.complex.GTVectorDataBinding;
  import org.opengis.feature.simple.SimpleFeature;
  import org.w3c.dom.Node;
  
  import com.vividsolutions.jts.geom.Coordinate;
  import com.vividsolutions.jts.geom.Geometry;
  import com.vividsolutions.jts.geom.LineString;
  import com.vividsolutions.jts.geom.MultiLineString;
  import com.vividsolutions.jts.geom.MultiPolygon;
  import com.vividsolutions.jts.geom.Point;
  import com.vividsolutions.jts.geom.Polygon;
  
  public class SimpleGMLGenerator extends AbstractGenerator {
  	
  	
  	public SimpleGMLGenerator() {
  		super();
  		supportedIDataTypes.add(GTVectorDataBinding.class);
  	}
  	
  	@Override
  	public InputStream generateStream(IData data, String mimeType, String schema) throws IOException {
  		
  		File tempFile = null;
  		InputStream stream = null;
  		
  		try {
  			tempFile = File.createTempFile("gml", "xml");
  			this.finalizeFiles.add(tempFile);
  			FileOutputStream outputStream = new FileOutputStream(tempFile);
  			this.writeToStream(data, outputStream);
  			outputStream.flush();
  			outputStream.close();
  			
  			stream = new FileInputStream(tempFile);
  		} catch (IOException e){
  			throw new IOException("Unable to generate GML");
  		}
  		
  		return stream;
  	}
  
  	public Node generateXML(IData coll, String schema) {
  		return generateXMLObj(coll, schema).getDomNode();
  	}
  
  	public void write(IData coll, Writer writer) {
  		GMLPacketDocument doc = generateXMLObj(coll, null);
  		try {
  			BufferedWriter bufferedWriter = new BufferedWriter(writer);
  			bufferedWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  			doc.save(bufferedWriter);
  			bufferedWriter.close();
  		}
  		catch(IOException e) {
  			throw new RuntimeException(e);
  		}
  	}
  
  	private GMLPacketDocument generateXMLObj(IData coll, String schema2) {
  		GMLPacketDocument doc = GMLPacketDocument.Factory.newInstance();
  		GMLPacketType packet = doc.addNewGMLPacket();
  		if(coll == null) {
  			return doc;
  		}
  		FeatureIterator<?> iter = ((GTVectorDataBinding)coll).getPayload().features();
  		while(iter.hasNext()) {
  			SimpleFeature feature = (SimpleFeature) iter.next();
  			StaticFeatureType staticFeature = packet.addNewPacketMember().addNewStaticFeature();
  			Geometry geom = (Geometry) feature.getDefaultGeometry();
  			String geomType = geom.getGeometryType();
  			if(geomType.equals("Point")) {
  				Point point = (Point)geom;
  				Coordinate coord = point.getCoordinate();
  				if (coord != null) {
  					PointPropertyType pointType = staticFeature.addNewPointProperty();
  					pointType.addNewPoint().setCoord(convertToXMLCoordType(coord));
  					generateAttribute(feature, staticFeature);					
  					}
  			}
  			else if(geomType.equals("LineString")) {
  				LineString ls = (LineString)geom;
  				CoordType[] coords = convertToXMLCoordType(ls.getCoordinates());
  				if(coords != null) {
  					ls.getCoordinates();
  					LineStringPropertyType lsType = staticFeature.addNewLineStringProperty();
  					lsType.addNewLineString().setCoordArray(coords);
  					
  					generateAttribute(feature, staticFeature);	
  				}
  			}
  			else if(geomType.equals("Polygon")) {
  				Polygon polygon = (Polygon)geom;
  				PolygonType xmlPolygon = staticFeature.addNewPolygonProperty().addNewPolygon();
  				xmlPolygon.setOuterBoundaryIs(convertToXMLLinearRing(polygon.getExteriorRing()));
  				LinearRingMemberType innerBoundary = xmlPolygon.addNewInnerBoundaryIs();
  				for(int i = 0; i < polygon.getNumInteriorRing(); i++) {
  					LinearRingType innerRing = innerBoundary.addNewLinearRing();
  					innerRing.setCoordArray(convertToXMLCoordType(polygon.getInteriorRingN(i).getCoordinates()));
  				}
  				generateAttribute(feature, staticFeature);	
  			}
  			else if (geomType.equals("MultiPolygon")) {
  				MultiPolygon mp = (MultiPolygon)geom;
  				for(int i = 0; i < mp.getNumGeometries(); i++) {
  					if(i > 0) {
  						staticFeature = packet.addNewPacketMember().addNewStaticFeature();
  					}
  					Polygon p = (Polygon) (geom.getGeometryN(i));
  					
  					PolygonType pType = staticFeature.addNewPolygonProperty().addNewPolygon();
  					pType.setOuterBoundaryIs(convertToXMLLinearRing(p.getExteriorRing()));
  					LinearRingMemberType innerBoundary = pType.addNewInnerBoundaryIs();
  					for(int j = 0; j < p.getNumInteriorRing(); j++) {
  						LinearRingType innerRing = innerBoundary.addNewLinearRing();
  						innerRing.setCoordArray(convertToXMLCoordType(p.getInteriorRingN(j).getCoordinates()));
  					}
  					
  				}
  				generateAttribute(feature, staticFeature);	
  			}
  			// THE MULTILINESTRING WILL BE DEVIDED INTO NORMAL LINESTRINGs, 
  			else if(geomType.equals("MultiLineString")) {
  				MultiLineString mls = (MultiLineString)geom;
  				for(int i = 0; i < mls.getNumGeometries(); i++) {
  					if(i > 0) {
  						staticFeature = packet.addNewPacketMember().addNewStaticFeature();
  					}
  					LineString ls = (LineString) (geom.getGeometryN(i));
  					LineStringPropertyType lsType = staticFeature.addNewLineStringProperty();
  					lsType.addNewLineString().setCoordArray(convertToXMLCoordType(ls.getCoordinates()));
  				}
  				generateAttribute(feature, staticFeature);	
  			}
  //			else if(geomType.equals("GeometryCollection")) {
  //				GeometryCollection geomColl = (GeometryCollection)geom;
  //				geomColl.get
  //			}
  			else if(geom.isEmpty()) {
  				//GEOMETRY is empty, do nothing
  				
  			}
  			else {
  				throw new IllegalArgumentException("geometryType not supported: " + geomType);
  			}
  		}
  		return doc;
  	}
  
  	private void generateAttribute(SimpleFeature feature,
  			StaticFeatureType staticFeature) {
  		if(feature.getFeatureType().getAttributeCount()>1){
  			
  			PropertyType propertyType;
  			Value value;
  			int attributePosCounter=0;
  			for (Object o: feature.getAttributes()) {
  				DataType.Enum dataType;
  				if(o instanceof Integer){
  					dataType = DataType.INTEGER;
  				}else if(o instanceof String){
  					dataType = DataType.STRING;
  				}else if(o instanceof Boolean){
  					dataType = DataType.BOOLEAN;
  				}else if(o instanceof Long){
  					dataType = DataType.LONG;
  				}else if(o instanceof Double){
  					dataType = DataType.DECIMAL;
  				}
  				else continue;	//Don't create anything
  				
  				propertyType = staticFeature.addNewProperty();
  				propertyType.setPropertyName(feature.getFeatureType().getAttributeDescriptors().get(attributePosCounter).getLocalName());
  				value = propertyType.addNewValue();
  				value.setDataType(dataType);
  				value.setStringValue(String.valueOf(o));
  				attributePosCounter++;
  			}
  		}
  	}
  	
  	private LinearRingMemberType convertToXMLLinearRing(LineString ls) {
  		LinearRingMemberType ringMember = LinearRingMemberType.Factory.newInstance();
  		LinearRingType ring = LinearRingType.Factory.newInstance();
  		CoordType[] coords = convertToXMLCoordType(ls.getCoordinates());
  		if(coords == null) {
  			return null;
  		}
  		ring.setCoordArray(coords);
  		ringMember.setLinearRing(ring);
  		return ringMember;
  	}
  	
  	private CoordType[] convertToXMLCoordType(Coordinate[] coords) {
  		ArrayList<CoordType> coordsList = new ArrayList<CoordType>();
  		for(int i = 0; i < coords.length; i++) {
  			CoordType tempCoord = convertToXMLCoordType(coords[i]);
  			if(tempCoord != null) {
  				coordsList.add(tempCoord);
  			}
  		}
  		if(coordsList.isEmpty()) {
  			return null;
  		}
  		CoordType[] returnCoords = new CoordType[coordsList.size()];
  		returnCoords = coordsList.toArray(returnCoords);
  		return returnCoords;
  	}
  	
  	private CoordType convertToXMLCoordType(Coordinate coord) {
  		if(Double.isNaN(coord.x) || Double.isNaN(coord.y)) {
  			return null;
  		}
  		CoordType xmlCoord = CoordType.Factory.newInstance();
  		try {
  			xmlCoord.setX(new BigDecimal(Double.toString(coord.x)));
  			xmlCoord.setY(new BigDecimal(Double.toString(coord.y)));
  		}
  		catch(NumberFormatException e) {
  			throw new IllegalArgumentException(e);
  		}
  		if(!Double.isNaN(coord.z)) {
  			xmlCoord.setZ(BigDecimal.valueOf(coord.z));
  		}
  		return xmlCoord;
  	}
  
  	public void writeToStream(IData coll, OutputStream os) {
  		OutputStreamWriter w = new OutputStreamWriter(os);
  		write (coll, w);		
  	}
  
  }