package kr.gmtc.gw.elastic.vo.AnalyzeArea;

import java.util.List;

import org.locationtech.jts.algorithm.ConvexHull;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.operation.buffer.BufferOp;
import org.locationtech.jts.operation.buffer.BufferParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import kr.gmtc.gw.elastic.utils.CommonUtils;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Area {
	
//	private static final GeometryFactory gf = new GeometryFactory(
//			new PrecisionModel(PrecisionModel.FLOATING),
//			4326,
//			CoordinateArraySequenceFactory.instance());
	private static final GeometryFactory gf = new GeometryFactory();
	private static final Logger logger = LoggerFactory.getLogger(Area.class);
	
	private boolean isValid = false;
	private String id;
	private String name;
	private String areatype;
	private Polygon polygon;
	private List<AreaPoint> points;
	
	private LineString line;
	
	public int addPoint(AreaPoint ponit) {
		try {
			this.points.add(ponit);
			return this.points.size();
		} catch (NullPointerException ne) {
			return -1;
		} catch (RuntimeException re) {
			return this.points.size();
		}		
	}
	
	public void generatePolygon() {
		try {
			this.isValid = false;
			
			if (points.size() > 1) {
				
				AreaPoint point = null;
				Coordinate[] coords = new Coordinate[points.size()+1];
				
				for (int i = 0; i < points.size(); i++) {
					point = points.get(i);					
					if (point == null) {
						return;
					}					
					coords[i] = new Coordinate(point.getLon(), point.getLat());
				}
				coords[points.size()] = new Coordinate(coords[0].x, coords[0].y);
				
				ConvexHull convexHull = new ConvexHull(coords, gf);
				Geometry convexHullGeometry = convexHull.getConvexHull();
				Coordinate[] sortedCoordinates = convexHullGeometry.getCoordinates();
				this.polygon = gf.createPolygon(sortedCoordinates);
				
//				LinearRing ring = gf.createLinearRing(coords);
//				this.polygon = gf.createPolygon(ring);
				this.isValid = true;
				
//				logger.info("Generated Polygon ["+ this.name +"] : " + this.polygon);
			}	
			
		} catch (RuntimeException e) {
			logger.error("Polygon generated fail : ["+ this.id +"] "+ name, e);
		}
	}
	
	public void generateLineToBufferedPolygon(double bufferMeter) {
		try {
			this.isValid = false;
			Coordinate[] coords = this.points.stream()
					.map(point -> new Coordinate(point.getLon(), point.getLat()))
					.toArray(i -> new Coordinate[i]);
			
			LineString line = gf.createLineString(coords);
			this.line = gf.createLineString(coords);
			
			Geometry buffer = BufferOp.bufferOp(line, CommonUtils.meterOnWGS84(bufferMeter), BufferParameters.CAP_SQUARE);
			this.polygon = gf.createPolygon(buffer.getCoordinates());
			this.isValid = true;
			
//			logger.info("Taxiway buffered polygon info : "+ this.polygon);
			
		} catch (RuntimeException e) {
			logger.error("Line to Buffered Polygon generated fail : ["+ this.id +"] "+ name, e);
		}
	}
	
	public boolean isContains(Point point) throws RuntimeException {
		if (this.isValid == false) {
			return false;
		}
		return this.polygon.contains(point);
	}

	public void generateBufferedPolygon(double bufferMeter) {
		try {
			generatePolygon();
			
			this.isValid = false;
			
			Geometry buffer = BufferOp.bufferOp(this.polygon, CommonUtils.meterOnWGS84(bufferMeter), BufferParameters.CAP_SQUARE);
			this.polygon = gf.createPolygon(buffer.getCoordinates());
			this.isValid = true;
			
		} catch (RuntimeException e) {
			logger.error("Buffered Polygon generated fail : ["+ this.id +"] "+ name, e);
		} 
	}
}
