package com.persagy.proxy.adm.utils;

import cn.hutool.core.collection.CollUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.jts.geom.*;

import java.util.ArrayList;
import java.util.List;


/**
 * @ClassName GeoToolsUtil
 * @Description: 点面包含关系分析
 * @Author linhuili
 * @Date 2021/9/3 20:31
 * @Version V1.0
 **/
@Slf4j
public class GeoToolsUtil {
    /**
     * 创建点
     * @param pointLocal
     */
    private static Point createPoint(ObjectNode pointLocal){
        double x =0.0D;
        double y =0.0D;
        double z =0.0D;
        if(pointLocal.get("x") != null){
            x = pointLocal.get("x").asDouble();
        }
        if(pointLocal.get("y") != null){
            y = pointLocal.get("y").asDouble();
        }
        if(pointLocal.get("z") !=null){
            z = pointLocal.get("z").asDouble();
        }
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        Coordinate coord = new Coordinate(x, y,z);
        Point point = geometryFactory.createPoint(coord);
        return point;
    }

    /**
     * 创建面
     * @param outLines
     */
    private static Polygon createPolygon(List<ObjectNode> outLines){
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        List<Coordinate> objects = new ArrayList<>();
        for (ObjectNode outLine : outLines) {
            double x = 0.0D;
            double y = 0.0D;
            double z = 0.0D;
            if(outLine.get("x") != null){
                x =outLine.get("x").asDouble();
            }
            if(outLine.get("y") != null){
                y = outLine.get("y").asDouble();
            }
            if(outLine.get("z") !=null){
                z = outLine.get("z").asDouble();
            }
            Coordinate coordinate = new Coordinate(x, y, z);
            objects.add(coordinate);
        }
        //注意数据的闭合
        Coordinate[] coordinates = objects.toArray(new Coordinate[0]);
        LinearRing ring = geometryFactory.createLinearRing(coordinates);
        LinearRing holes[] = null;
        Polygon polygon = geometryFactory.createPolygon(ring, holes);

        return polygon;
   }
    /**
     * 创建面
     * @param outLines
     */
    private static Polygon createPolygon(ArrayNode outLines){
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        List<Coordinate> objects = new ArrayList<>();
        for (JsonNode outLine : outLines) {
            double x = outLine.has("X")?Double.parseDouble(outLine.get("X").asText()):0.0D;
            double y = outLine.has("Y")?Double.parseDouble(outLine.get("Y").asText()):0.0D;
            double z = 0L;
            Coordinate coordinate = new Coordinate(x, y, z);
            objects.add(coordinate);
        }
        //注意数据的闭合
        Coordinate[] coordinates = objects.toArray(new Coordinate[0]);
        LinearRing ring = geometryFactory.createLinearRing(coordinates);
        return geometryFactory.createPolygon(ring, null);
    }

    /**
     * 点面包含关系判断
     * 用途：判断一个面是否包含一个点，即一个点是否在一个面内
     * @param pointLocal 坐标点
     * @param outLines 轮廓线
     * @return
     */
   public static Boolean isPointInPoly(ObjectNode pointLocal, List<ObjectNode> outLines) {
       //创建点
       Point point = createPoint(pointLocal);
       //创建面
       Polygon polygon = createPolygon(outLines);
       //判断点面包含关系
       boolean flag = polygon.contains(point);
       return flag;
   }


    /**
     * 点面包含关系判断
     * 用途：判断一个面是否包含一个点，即一个点是否在一个面内
     * @param fromOutLines: 轮廓坐标
     * @param toOutLines: 轮廓坐标
     * @return
     */
    public static Boolean isPolyInPoly(List<ObjectNode> fromOutLines,
                                       List<ObjectNode> toOutLines) {
        if (CollUtil.isEmpty(fromOutLines) || CollUtil.isEmpty(toOutLines)){
            return false;
        }
        //创建点
        Polygon fromPolygon = createPolygon(fromOutLines);
        //创建面
        Polygon toPolygon = createPolygon(toOutLines);
        //判断面-面包含关系
        return fromPolygon.contains(toPolygon)
                || fromPolygon.equalsTopo(toPolygon)
                || fromPolygon.overlaps(toPolygon)
                || toPolygon.contains(fromPolygon);
    }

    /**
     * 点面包含关系判断
     * 用途：判断一个面是否包含一个点，即一个点是否在一个面内
     * @param pointLocal 坐标点
     * @param outLines 轮廓线
     * @return
     */
    public static Boolean isPointInPoly(ObjectNode pointLocal, ArrayNode outLines) {
        if (null==pointLocal || CollUtil.isEmpty(outLines)){
            return false;
        }
        //创建点
        Point point = createPoint(pointLocal);
        if (!outLines.get(0).isArray()){
            return isPointInPoly(point,outLines);
        }
        for (JsonNode outLine : outLines) {
            if (outLine.isArray() && outLine.get(0).isArray()){
                ArrayNode subOutlines = (ArrayNode)outLine;
                for (JsonNode subOutLine : subOutlines) {
                    //创建面
                    if (isPointInPoly(point,(ArrayNode) subOutLine)){
                        return true;
                    }
                }
                continue;
            }
            //创建面
            if (isPointInPoly(point,(ArrayNode)outLine)){
                return true;
            }
        }
        //判断点面包含关系
        return false;
    }
    /**
     * 点面包含关系判断
     * 用途：判断一个面是否包含一个点，即一个点是否在一个面内
     * @param point 坐标点
     * @param outLines 轮廓线
     * @return
     */
    public static Boolean isPointInPoly(Point point, ArrayNode outLines) {
        if (null==point || CollUtil.isEmpty(outLines)){
            return false;
        }
        //创建面
        try {
            Polygon polygon = createPolygon(outLines);
            // 判断点面包含关系
            return polygon.contains(point);
        }catch (IllegalArgumentException argumentE){
            try {
                // 将第一个加入最后一个结点尝试一下
                outLines.add(outLines.get(0));
                Polygon polygon = createPolygon(outLines);
                // 判断点面包含关系
                return polygon.contains(point);
            }catch (Exception ignored){}
        } catch (Exception e){
            log.warn("解析失败"+outLines.toString(),e);
        }
        return false;
    }

}
