package com.huashe.park.common.geo;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.huashe.park.common.geo.convex.ConcaveHull;
import com.huashe.park.common.geo.convex.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson2.JSONObject;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

public class ConvexHullUtil {
    private static final Logger logger = LoggerFactory.getLogger(Point.class);

    private double x;

    private double y;

    public static List<Point> convexHull(List<Point> points) {
        // 小于三个点的情况，直接返回
        if (points.size() < 3) {
            return points;
        }

        Point first = points.get(0);
        // 找到最左下角的点
        for (int i = 0; i < points.size(); i++) {
            if (points.get(i).getY() < first.getY()
                || (points.get(i).getY() == first.getY() && points.get(i).getX() < first.getX())) {
                first = points.get(i);
            }
        }
        logger.info("凸包计算-最下面偏左的点位是：{}", first);
        List<Point> sortedList = angleSorting(first, points);
        logger.info("凸包计算-角排序结果：{}", JSONObject.toJSONString(sortedList));
        int firstIndex = 0;
        for (int i = 0; i < sortedList.size(); i++) {
            if (first.getX() == sortedList.get(i).getX() && first.getY() == sortedList.get(i).getY()) {
                firstIndex = i;
            }
        }
        // 结果集合
        List<Point> convexHullPoints = new ArrayList<>();
        convexHullPoints.add(first);
        int nextIndex = firstIndex + 1 == sortedList.size() ? 0 : firstIndex + 1;
        Point next = new Point();
        while (next != first) {
            next = sortedList.get(nextIndex);

            while (true) {
                if (convexHullPoints.size() < 2) {
                    convexHullPoints.add(next);
                    break;
                }
                if (isEnabled(convexHullPoints.get(convexHullPoints.size() - 2),
                    convexHullPoints.get(convexHullPoints.size() - 1), next)) {
                    convexHullPoints.add(next);
                    break;
                }
                else {
                    convexHullPoints.remove(convexHullPoints.size() - 1);
                }
            }
            nextIndex = nextIndex + 1 == sortedList.size() ? 0 : nextIndex + 1;
        }
        convexHullPoints = convexHullPoints.stream().distinct().collect(Collectors.toList());
        logger.info("凸包计算-结果:{}", JSONObject.toJSONString(convexHullPoints));
        logger.info("凸包结算-结果绘制成线:{}", point2WktLine(convexHullPoints));
        return convexHullPoints;
    }

    public static boolean isEnabled(Point A, Point B, Point C) {
        double mulCross = A.getX() * B.getY() - A.getY() * B.getX() + B.getX() * C.getY() - B.getY() * C.getX()
            + C.getX() * A.getY() - C.getY() * A.getX();
        if (mulCross >= 0) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * 以原点为中心进行角排序
     * 
     * @param points 点位
     * @return 排序的点位
     */
    public static List<Point> angleSorting(Point first, List<Point> points) {
        points = points.stream().sorted(Comparator.comparing(Point::getX)).collect(Collectors.toList());
        points = points.stream().sorted(Comparator.comparing(Point::getY)).collect(Collectors.toList());
        Map<Double, Point> pointMap = new HashMap<>();
        List<Double> angles = new ArrayList<>();
        for (Point point : points) {
            double angle = Math.atan2(point.getY() - first.getY(), point.getX() - first.getX()) * 180.0 / Math.PI;
            if (angle < 0) {
                angle += 360.0;
            }
            pointMap.put(angle, point);
            angles.add(angle);
        }
        angles = angles.stream().sorted().collect(Collectors.toList());
        List<Point> result = new ArrayList<>();
        for (Double angle : angles) {
            result.add(pointMap.get(angle));
        }
        return result;
    }

    public static String point2WktLine(List<Point> points) {
        StringBuilder sb = new StringBuilder("LINESTRING(");
        for (Point point : points) {
            sb.append(point.getX()).append(" ").append(point.getY()).append(", ");
        }
        sb.append(points.get(0).getX()).append(" ").append(points.get(0).getY()).append(")");
        return sb.toString();
    }

    // public static List<Point> point2Wgs84(List<Point> points) {
    // List<Point> coordinates = new ArrayList<>();
    // for (Point point: points) {
    // Coordinate coordinate = new Coordinate(point.getX(), point.getY(),0D);
    // Coordinate cover = CoordinateUtils.gaussianToWgs84(coordinate, 117);
    // coordinates.add(new Point(cover.getX(), cover.getY()));
    // }
    // return coordinates;
    // }

    public static String point2WktPoint(List<Point> points) {
        StringBuilder sb = new StringBuilder("MULTIPOINT(");
        for (Point point : points) {
            sb.append(point.getX()).append(" ").append(point.getY()).append(", ");
        }
        sb.append(points.get(0).getX()).append(" ").append(points.get(0).getY()).append(")");
        return sb.toString();
    }



    public static void main(String[] args) {
        List<Point> points = new ArrayList<>();
        points.add(new Point(116.51420419741034, 39.72464578059458));
        points.add(new Point(116.5142100494552, 39.72461905019859));
        points.add(new Point(116.51421804106533, 39.724615458020104));
        points.add(new Point(116.51421649139034, 39.72462206236937));
        points.add(new Point(116.51422472532401, 39.724611537067666));
        points.add(new Point(116.51422189537502, 39.72459915116547));
        points.add(new Point(116.51421999329702, 39.724606604151624));
        points.add(new Point(116.51420852801604, 39.72461304201025));
        points.add(new Point(116.51420145678534, 39.72464519351396));
        ConcaveHull concaveHull = new ConcaveHull(points);
        List<Point> result = concaveHull.Compute(-1D);
        logger.info("凸包计算-结果:{}", point2WktLine(result));
        logger.info("原始点位：{}", point2WktPoint(points));
        logger.info("原始点位连线：{}", point2WktLine(points));
        convexHull(points).stream().distinct().collect(Collectors.toList());
    }
}
