package com.persagy.dmp.rwd.basic.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.LinkedHashMap;
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 =0L;
        double y =0L;
        double z =0L;
        if(pointLocal.get("X") != null){
            x =pointLocal.get("X").asDouble();
        }
        if(pointLocal.get("Y") != null){
            y = pointLocal.get("Y").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(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, 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 (null==outLine || null==outLine.get(0)){
               continue;
           }
           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;
    }
    /**
     * 面面包含关系判断
     * 用途：判断一个面是否包含一个点，即一个点是否在一个面内
     * @param fromOutLines: 轮廓坐标
     * @param toOutLines: 轮廓坐标
     * @return
     */
   public static Boolean isPolyInPoly(ArrayNode fromOutLines, ArrayNode toOutLines) {
       if (CollUtil.isEmpty(fromOutLines) || CollUtil.isEmpty(toOutLines)){
           return false;
       }
       if (!fromOutLines.get(0).isArray()){
           return isPolyInPolyInternal(fromOutLines,toOutLines);
       }
       for (JsonNode outLine : fromOutLines) {
           if (outLine.isArray() && outLine.get(0).isArray()){
               ArrayNode subOutlines = (ArrayNode)outLine;
               for (JsonNode subOutLine : subOutlines) {
                   //创建面
                   if (isPolyInPolyInternal((ArrayNode) subOutLine,toOutLines)){
                       return true;
                   }
               }
               continue;
           }
           //创建面
           if (isPolyInPolyInternal((ArrayNode)outLine,toOutLines)){
               return true;
           }
       }
       return false;
   }
    /***
     * Description: 判断面与面是否有交集
     * @param fromOutLines : 左边的轮廓坐标数组
     * @param toOutLines : 右边的轮廓坐标数组
     * @return : boolean
     * @author : lijie
     * @date :2021/11/24 16:22
     * Update By lijie 2021/11/24 16:22
     */
    private static Boolean isPolyInPolyInternal(ArrayNode fromOutLines, ArrayNode toOutLines) {
        if (!toOutLines.get(0).isArray()){
            return decidePolyInPoly(fromOutLines,toOutLines);
        }
        for (JsonNode outLine : toOutLines) {
            if (outLine.isArray() && outLine.get(0).isArray()){
                ArrayNode subOutlines = (ArrayNode)outLine;
                for (JsonNode subOutLine : subOutlines) {
                    //创建面
                    if (decidePolyInPoly(fromOutLines,(ArrayNode) subOutLine)){
                        return true;
                    }
                }
                continue;
            }
            //创建面
            if (decidePolyInPoly(fromOutLines,(ArrayNode)outLine)){
                return true;
            }
        }
        //判断点面包含关系
        return false;
    }
    /***
     * Description: 判断面与面是否有交集
     * @param fromOutLines : 左边的轮廓坐标数组
     * @param toOutLines : 右边的轮廓坐标数组
     * @return : boolean
     * @author : lijie
     * @date :2021/11/24 16:22
     * Update By lijie 2021/11/24 16:22
     */
    private static boolean decidePolyInPoly(ArrayNode fromOutLines, ArrayNode toOutLines) {
        //创建点
        Polygon fromPolygon = null;
        Polygon toPolygon = null;
        try {
            fromPolygon = createPolygon(fromOutLines);
        }catch (IllegalArgumentException argumentE){
            try {
                // 将第一个加入最后一个结点尝试一下
                fromOutLines.add(fromOutLines.get(0));
                fromPolygon = createPolygon(fromOutLines);
            }catch (Exception ignored){}
        } catch (Exception e){
            return false;
        }
        try {
            toPolygon = createPolygon(toOutLines);
        }catch (IllegalArgumentException argumentE){
            try {
                // 将第一个加入最后一个结点尝试一下
                toOutLines.add(toOutLines.get(0));
                toPolygon = createPolygon(toOutLines);
            }catch (Exception ignored){}
        } catch (Exception e){
            return false;
        }
        if (null==fromPolygon || null==toPolygon){
            return false;
        }
        return fromPolygon.contains(toPolygon)
                || fromPolygon.equalsTopo(toPolygon)
                || fromPolygon.overlaps(toPolygon)
                || toPolygon.contains(fromPolygon);
    }

}
