package com.leromro.common.utils;

import java.util.Arrays;
import java.util.List;

public class GeoUtils {

    private static final int EARTH_RADIUS_METERS = 6371000; // 地球半径（米）

    /**
     * 计算两点之间的距离（Haversine公式）
     *
     * @param lat1 纬度1
     * @param lon1 经度1
     * @param lat2 纬度2
     * @param lon2 经度2
     * @return 距离（米）
     */
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        double latDistance = Math.toRadians(lat2 - lat1);
        double lonDistance = Math.toRadians(lon2 - lon1);
        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
                + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
                * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return EARTH_RADIUS_METERS * c;
    }

    /**
     * 判断一个点是否在圆形围栏内
     *
     * @param pointLat 点纬度
     * @param pointLon 点经度
     * @param centerLat 围栏中心纬度
     * @param centerLon 围栏中心经度
     * @param radiusMeters 围栏半径（米）
     * @return 是否在围栏内
     */
    public static boolean isInsideCircle(double pointLat, double pointLon, double centerLat, double centerLon, double radiusMeters) {
        double distance = calculateDistance(pointLat, pointLon, centerLat, centerLon);
        return distance <= radiusMeters;
    }

    /**
     * 判断一个点是否在多边形围栏内（适用于任意多边形）
     *
     * @param pointLat 点纬度
     * @param pointLon 点经度
     * @param polygon 多边形顶点列表（格式为 [lat1, lon1, lat2, lon2, ...]） ？？？？应该是：[[lat1, lon1],[lat2, lon2]] 吧
     * @return 是否在多边形内
     */
    public static boolean isInsidePolygon(double pointLat, double pointLon, List<double[]> polygon) {
        int n = polygon.size();
        boolean inside = false;
        for (int i = 0, j = n - 1; i < n; j = i++) {
            double xi = polygon.get(i)[0], yi = polygon.get(i)[1];
            double xj = polygon.get(j)[0], yj = polygon.get(j)[1];

            boolean intersect = ((yi > pointLon) != (yj > pointLon))
                    && (pointLat < (xj - xi) * (pointLon - yi) / (yj - yi) + xi);
            if (intersect) inside = !inside;
        }
        return inside;
    }

    /**
     * 判断一个点是否在多边形或圆形围栏内
     *
     * @param pointLat 点纬度
     * @param pointLon 点经度
     * @param fenceType 类型："circle" 或 "polygon"
     * @param args 可变参数：
     *             - 如果是 circle: centerLat, centerLon, radiusMeters
     *             - 如果是 polygon: List<double[]> polygon
     * @return 是否在围栏内
     */
    public static boolean isInFence(double pointLat, double pointLon, String fenceType, Object... args) {
        switch (fenceType.toLowerCase()) {
            case "circle":
                if (args.length == 3 && args[0] instanceof Double && args[1] instanceof Double && args[2] instanceof Double) {
                    double centerLat = (Double) args[0];
                    double centerLon = (Double) args[1];
                    double radius = (Double) args[2];
                    return isInsideCircle(pointLat, pointLon, centerLat, centerLon, radius);
                }
                break;
            case "polygon":
                if (args.length == 1 && args[0] instanceof List<?>) {
                    @SuppressWarnings("unchecked")
                    List<double[]> polygon = (List<double[]>) args[0];
                    return isInsidePolygon(pointLat, pointLon, polygon);
                }
                break;
            default:
                throw new IllegalArgumentException("Unsupported fence type: " + fenceType);
        }
        return false;
    }

    public static void main(String[] args) {
        // 两点间距离
        double distance = GeoUtils.calculateDistance(39.9042, 116.4074, 31.2304, 121.4737); // 北京到上海
        System.out.println("Distance: " + distance + " meters");

        // 点是否在圆形内
        boolean inCircle = GeoUtils.isInsideCircle(39.905, 116.408, 39.9042, 116.4074, 1000); // 半径1000米
        System.out.println("In Circle: " + inCircle);

        // 点是否在多边形内
        List<double[]> polygon = Arrays.asList(
                new double[]{39.904, 116.407},
                new double[]{39.905, 116.407},
                new double[]{39.905, 116.408},
                new double[]{39.904, 116.408}
        );

        boolean inPolygon = GeoUtils.isInsidePolygon(39.9045, 116.4075, polygon);
        System.out.println("In Polygon: " + inPolygon);
    }

}