package com.esri.geoevent.solutions.processor.geometry;

/*
 * #%L
 * Esri :: AGES :: Solutions :: Processor :: Geometry
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2013 - 2014 Esri
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;

import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.MapGeometry;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.map.FeatureSet;
import com.esri.core.map.Graphic;
import com.esri.ges.core.ConfigurationException;
import com.esri.ges.core.component.ComponentException;
import com.esri.ges.core.geoevent.FieldException;
import com.esri.ges.core.geoevent.GeoEvent;
import com.esri.ges.manager.geoeventdefinition.GeoEventDefinitionManager;
import com.esri.ges.processor.GeoEventProcessorBase;
import com.esri.ges.processor.GeoEventProcessorDefinition;
import com.esri.ges.spatial.Spatial;

public class VisibilityProcessor extends GeoEventProcessorBase {
	private static final Log LOG = LogFactory.getLog(RangeFanProcessor.class);
	public Spatial spatial;
	public GeoEventDefinitionManager manager;
	private SpatialReference srIn;
	private SpatialReference srBuffer;
	private SpatialReference srOut;
	public VisibilityProcessor(GeoEventProcessorDefinition definition, Spatial s, GeoEventDefinitionManager m)
			throws ComponentException {
		super(definition);
		spatial = s;
		manager = m;
		//tagMgr=tm;
		geoEventMutator= true;
	}

	@Override
	public GeoEvent process(GeoEvent ge) throws Exception {
		String gp = properties.get("gpservice").getValue().toString();
		String is = properties.get("imageservice").getValue().toString();
		
		
		double radius;
		String radiusSource = properties.get("radiusSource").getValue().toString();
		if(radiusSource.equals("Constant"))
		{
			radius = (Double) properties.get("radius").getValue();
		}
		else
		{
			String eventfld = properties.get("radiusEvent").getValue().toString();
			String[] arr = eventfld.split(":");
			radius = (Double)ge.getField(arr[1]);
		}
		String unit = properties.get("units").getValue().toString();
		double elevation;
		String elevationSource = properties.get("elevationSource").getValue().toString();
		if(elevationSource.equals("Constant")){
			elevation = (Double)properties.get("elevation").getValue();
		}
		else
		{
			String eventfld = properties.get("elevationEvent").getValue().toString();
			String[] arr = eventfld.split(":");
			elevation = (Double)ge.getField(arr[1]);
		}
		String units_elev = properties.get("units_elev").getValue().toString();
		int inwkid = (Integer) properties.get("wkidin").getValue();
		int outwkid = (Integer) properties.get("wkidout").getValue();
		int bufferwkid = (Integer) properties.get("wkidbuffer").getValue();
		srIn = SpatialReference.create(inwkid);
		srBuffer = SpatialReference.create(bufferwkid);
		srOut = SpatialReference.create(outwkid);
		
		ConstructVisibilityRest(ge, gp, is, radius, unit,  elevation, units_elev, bufferwkid);
		return ge;
	}
	
	private void ConstructVisibilityRest(GeoEvent ge, String gpservice, String imageservice, double range, String unit,  double elevation, String units_elev, int wkid) throws UnsupportedEncodingException, IOException, ConfigurationException, FieldException 
	{
		UnitConverter uc = new UnitConverter();
		range = uc.Convert(range, unit, srBuffer);
		String procUnitName = srBuffer.getUnit().getName();
		// normalize horizontal and vertical units to z-factor = 1
		// double inElev = elevation;
		if (procUnitName.equals("Meter")) {
			if (units_elev.equals("Feet")) {
				elevation = elevation * 0.3048;
			}
		} else {
			if (units_elev.equals("Meters")) {
				elevation = elevation * 3.28084;
			}
		}
		
		com.esri.ges.spatial.Geometry eventGeo = ge.getGeometry();
		String jsonEvent = eventGeo.toJson();
		JsonFactory jf = new JsonFactory();
		JsonParser jp = jf.createJsonParser(jsonEvent);
		MapGeometry mapgeo = GeometryEngine.jsonToGeometry(jp);
		Geometry tmpmask = mapgeo.getGeometry();
		Geometry mask = GeometryEngine.project(tmpmask, srIn, srBuffer);
		Envelope extent = new Envelope();
		String obs = "";
		if(properties.get("observerSource").getValueAsString().equals("Geoevent"))
		{
			mask.queryEnvelope(extent);
			Double x = extent.getCenterX();
			Double y = extent.getCenterY();

			String cx = ((Double) x).toString();
			String cy = ((Double) y).toString();
			obs = cx + " " + cy;
		}
		else if(properties.get("observerSource").getValueAsString().equals("Event Field"))
		{
			
			String xeventfld = properties.get("observerXEvent").getValue().toString();
			String[] arr = xeventfld.split(":");
			Double x = (Double)ge.getField(arr[1]);
			String yeventfld = properties.get("observerYEvent").getValue().toString();
			arr = yeventfld.split(":");
			Double y = (Double)ge.getField(arr[1]);
			Point p = new Point(x,y);
			p = (Point)GeometryEngine.project(p, srIn, srBuffer);
			obs = ((Double)p.getX()).toString() + " " + ((Double)p.getY()).toString();
		}
		else
		{
			Double x = (Double)properties.get("observerX").getValue();
			Double y = (Double)properties.get("observerY").getValue();
			Point p = new Point(x,y);
			p = (Point)GeometryEngine.project(p, srIn, srBuffer);
			obs = ((Double)p.getX()).toString() + " " + ((Double)p.getY()).toString();
		}
		String contentType = "application/json";
		HttpClient httpclient = new DefaultHttpClient();
		String observers = URLEncoder.encode(obs, "UTF-8");
		imageservice = URLEncoder.encode(imageservice, "UTF-8");
		String jsonGeo = URLEncoder.encode(
				GeometryEngine.geometryToJson(srBuffer, mask), "UTF-8");

		String args = "observers=" + observers + "&image_service_url="
				+ imageservice + "&radius=" + ((Double) range).toString()
				+ "&height=" + ((Double) elevation).toString() + "&json_mask="
				+ jsonGeo + "&wkid=" + ((Integer) wkid).toString() + "&f=json";
		String path = gpservice + "/execute?";
		String uri = path + args;
		com.esri.ges.spatial.Geometry visible = null;
		com.esri.ges.spatial.Geometry nonvisible = null;
		try {
			HttpPost httppost = new HttpPost(uri);
			httppost.setHeader("Accept", contentType);
			HttpResponse response = httpclient.execute(httppost);

			HttpEntity entity = response.getEntity();
			
			if (entity != null) {
				InputStream instream = entity.getContent();
				try {
					// instream.read();
					BufferedReader br = new BufferedReader(
							new InputStreamReader((instream)));
					String output = "";
					String ln;
					while ((ln = br.readLine()) != null) {
						output += ln;
					}
					jf = new JsonFactory();
					jp = jf.createJsonParser(output);
					ObjectMapper mapper = new ObjectMapper();
					@SuppressWarnings("unchecked")
					Map<String, Object> map = (Map<String, Object>)mapper.readValue(jp, Map.class);
					@SuppressWarnings("unchecked")
					ArrayList<Object> resString = (ArrayList<Object>)map.get("results");
					
					@SuppressWarnings("unchecked")
					Map<String, Object> r = (Map<String, Object>)resString.get(0);
					@SuppressWarnings("unchecked")
					Map<String,Object> val = (Map<String,Object>)r.get("value");
					String fsetJson = mapper.writeValueAsString(val);
					//String visRes = res.getResults().get(2);
					jp=jf.createJsonParser(fsetJson);
					FeatureSet fset = FeatureSet.fromJson(jp);
					for (Graphic graphic : fset.getGraphics()) {

						int code = (Integer) graphic.getAttributes().get(
								"gridcode");
						// com.esri.ges.spatial.Geometry tmpgesVis = null;
						// com.esri.ges.spatial.Geometry tmpgesNonVis =
						// null;
						if (code == 0) {
							Geometry tmpvis = graphic.getGeometry();
							Geometry vis = GeometryEngine.project(tmpvis,
									srBuffer, srOut);
							String json = GeometryEngine
									.geometryToJson(srOut, vis);

							visible = spatial.fromJson(json);

						} else {
							Geometry tmpnonvis = graphic.getGeometry();
							Geometry nonvis = GeometryEngine.project(
									tmpnonvis, srBuffer, srOut);
							String json = GeometryEngine.geometryToJson(srOut,
									nonvis);

							nonvisible = spatial.fromJson(json);

						}
					}
					
				} catch (IOException ex) {
					// In case of an IOException the connection will be
					// released
					// back to the connection manager automatically
					throw ex;
				} catch (RuntimeException ex) {
					// In case of an unexpected exception you may want to
					// abort
					// the HTTP request in order to shut down the underlying
					// connection immediately.
					httppost.abort();
					throw ex;
				} finally {
					// Closing the input stream will trigger connection
					// release
					try {
						instream.close();
					} catch (Exception ignore) {
					}
				}
			}
		}
		catch(Exception ex){}
		/*GeoEventDefinition geoDef = ge.getGeoEventDefinition();
		List<FieldDefinition>fieldDefs = geoDef.getFieldDefinitions();
		ArrayList<FieldDefinition> newFieldDefs = new ArrayList<FieldDefinition>();
		for(FieldDefinition fieldDef: fieldDefs)
		{
			newFieldDefs.add(fieldDef);
		}
		FieldDefinition visFldDef = new DefaultFieldDefinition("visible", FieldType.Geometry, (String)null);
		FieldDefinition nonvisFldDef = new DefaultFieldDefinition("nonvisible", FieldType.Geometry, (String)null);
		newFieldDefs.add(visFldDef);
		newFieldDefs.add(nonvisFldDef);
		geoDef.setFieldDefinitions(newFieldDefs);*/
		ge.setField("visible", visible);
		ge.setField("nonvisible", nonvisible);
		ge.setField("gcvis", new Integer(1));
		ge.setField("gcnvis", new Integer(0));
	}
	
	
		
	private String ConstructJsonMaskFromGeoEvent(GeoEvent ge) throws IOException
	{
		com.esri.ges.spatial.Geometry eventgeo = ge.getGeometry();
		String json = eventgeo.toJson();
		JsonFactory jf = new JsonFactory();
		JsonParser jp = jf.createJsonParser(json);
		MapGeometry mgeo = GeometryEngine.jsonToGeometry(jp);
		Geometry geo = mgeo.getGeometry();
		Geometry maskGeo = GeometryEngine.project(geo, srIn, srBuffer);
		return GeometryEngine.geometryToJson(srBuffer, maskGeo);
	}
	
	

}