package com.fuyuanshen.equipment.utils.map;

import java.util.List;

/**
 * @author: 默苍璃
 * @date: 2025-09-1110:13
 */
public class GeoFenceChecker {

    /**
     * 点是否在围栏内
     *
     * @param pointLat    点的纬度
     * @param pointLng    点的经度
     * @param fenceType   围栏类型：0 POLYGON 或  1 CIRCLE
     * @param coordinates 围栏坐标点列表
     * @param radius      圆形围栏半径（米），仅对CIRCLE类型有效
     * @return true表示在围栏内，false表示在围栏外
     */
    public static boolean isPointInFence(double pointLat, double pointLng,
                                         Integer fenceType,
                                         List<Coordinate> coordinates,
                                         Long radius) {
        if (fenceType == 0) {
            return isPointInPolygon(pointLat, pointLng, coordinates);
        } else if (fenceType == 1) {
            if (coordinates == null || coordinates.isEmpty() || radius == null) {
                return false;
            }
            // 圆形围栏只有一个坐标点（圆心）
            Coordinate center = coordinates.get(0);
            return isPointInCircle(pointLat, pointLng, center.lat, center.lng, radius);
        }
        return false;
    }

    /**
     * 使用射线投射算法判断点是否在多边形内
     *
     * @param pointLat 点的纬度
     * @param pointLng 点的经度
     * @param polygon  多边形顶点坐标列表
     * @return true表示在多边形内，false表示在多边形外
     */
    public static boolean isPointInPolygon(double pointLat, double pointLng,
                                           List<Coordinate> polygon) {
        if (polygon == null || polygon.size() < 3) {
            return false;
        }

        int intersectCount = 0;
        int vertexCount = polygon.size();

        // 遍历多边形的每条边
        for (int i = 0; i < vertexCount; i++) {
            Coordinate p1 = polygon.get(i);
            Coordinate p2 = polygon.get((i + 1) % vertexCount);

            // 检查点是否在边的y范围内
            if (p1.lat != p2.lat &&
                    (pointLat >= Math.min(p1.lat, p2.lat)) &&
                    (pointLat < Math.max(p1.lat, p2.lat))) {

                // 计算边与从点发出的水平射线的交点经度
                double intersectLng = (pointLat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;

                // 如果交点在点的右侧，则计数加1
                if (pointLng < intersectLng) {
                    intersectCount++;
                }
            }
        }

        // 奇数个交点表示点在多边形内
        return intersectCount % 2 == 1;
    }

    /**
     * 判断点是否在圆形内
     *
     * @param pointLat  点的纬度
     * @param pointLng  点的经度
     * @param centerLat 圆心纬度
     * @param centerLng 圆心经度
     * @param radius    半径（米）
     * @return true表示在圆内，false表示在圆外
     */
    public static boolean isPointInCircle(double pointLat, double pointLng,
                                          double centerLat, double centerLng,
                                          Long radius) {
        double distance = calculateDistance(pointLat, pointLng, centerLat, centerLng);
        return distance <= radius;
    }

    /**
     * 计算两点间距离（使用Haversine公式）
     *
     * @param lat1 点1纬度
     * @param lng1 点1经度
     * @param lat2 点2纬度
     * @param lng2 点2经度
     * @return 距离（米）
     */
    public static double calculateDistance(double lat1, double lng1, double lat2, double lng2) {
        final double EARTH_RADIUS = 6371000; // 地球半径（米）

        double dLat = Math.toRadians(lat2 - lat1);
        double dLng = Math.toRadians(lng2 - lng1);

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
                        Math.sin(dLng / 2) * Math.sin(dLng / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return EARTH_RADIUS * c;
    }

    /**
     * 坐标点类
     */
    public static class Coordinate {
        public double lat;
        public double lng;

        public Coordinate() {
        }

        public Coordinate(double lat, double lng) {
            this.lat = lat;
            this.lng = lng;
        }
    }
}