package com.huashe.park.common.geo;

import java.util.List;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GeoUtils {
    private static final Logger log = LoggerFactory.getLogger(GeoUtils.class);

    /**
     * geometryFactory
     */
    private static final GeometryFactory geometryFactory = new GeometryFactory();

    /**
     * Gets distance.
     *
     * @param lat1 the lat 1
     * @param lon1 the lon 1
     * @param lat2 the lat 2
     * @param lon2 the lon 2
     * @return the distance
     * @author chen.cheng
     */
    public static double getDistance(double lat1, double lon1, double lat2, double lon2) {
        Point p1 = geometryFactory.createPoint(new Coordinate(lon1, lat1));
        Point p2 = geometryFactory.createPoint(new Coordinate(lon2, lat2));
        return p1.distance(p2);
    }

    /**
     * Gets poly center. POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))
     *
     * @param wktPolygon the wkt polygon
     * @return the poly center
     * @author chen.cheng
     */
    public static Point getPolyCenter(String wktPolygon) {
        // 创建WKTReader对象
        WKTReader reader = new WKTReader(geometryFactory);
        // 从WKT字符串中读取几何对象
        Geometry geometry = null;
        try {
            geometry = reader.read(wktPolygon);
        }
        catch (ParseException e) {
            log.info("Invalid WKT string: " + e.getMessage());
        }
        // 获取中心点
        return geometry.getCentroid();
    }

    /**
     * getPolygon POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))
     *
     * @param wktPolygon the wkt polygon
     * @return the poly center
     * @author chen.cheng
     */
    public static Polygon getPolygon(String wktPolygon) {
        // 创建WKTReader对象
        WKTReader reader = new WKTReader(geometryFactory);
        // 从WKT字符串中读取几何对象
        Polygon geometry = null;
        try {
            geometry = (Polygon) reader.read(wktPolygon);
        }
        catch (ParseException e) {
            log.info("Invalid WKT string: " + e.getMessage());
        }
        // 获取中心点
        return geometry;
    }

    /**
     * 判断指定的GPS点是否在电子围栏内
     *
     * @param fencePointsList 包含电子围栏的经纬度数据的列表，格式为 "经度,纬度"
     * @param pointStr 指定的GPS点，格式为 "经度,纬度"
     * @return 如果在电子围栏内则返回true，否则返回false
     */
    public static boolean isPointInGeoFence(List<String> fencePointsList, String pointStr) {
        // 将电子围栏的经纬度数据转换为坐标数组
        Coordinate[] fencePoints = parseCoordinates(fencePointsList);

        // 将指定的GPS点转换为坐标
        Coordinate targetPoint = parseCoordinate(pointStr);

        // 创建电子围栏多边形
        Polygon geoFencePolygon = createPolygon(fencePoints);

        // 创建指定的GPS点
        Point point = geometryFactory.createPoint(targetPoint);

        // 检查指定的GPS点是否在电子围栏内
        return geoFencePolygon.contains(point);
    }

    /**
     * 判断指定的GPS点是否在电子围栏内
     *
     * @param geoFencePolygon geo fence polygon
     * @param pointStr 指定的GPS点，格式为 "经度,纬度"
     * @return 如果在电子围栏内则返回true ，否则返回false
     * @since 2.0.0
     */
    public static boolean isPointInGeoFence(Polygon geoFencePolygon, String pointStr) {
        // 将指定的GPS点转换为坐标
        Coordinate testPoint = parseCoordinate(pointStr);

        // 创建指定的GPS点
        Point point = geometryFactory.createPoint(testPoint);

        // 检查指定的GPS点是否在电子围栏内
        return geoFencePolygon.contains(point);
    }

    public static boolean isPointInGeoFence(Polygon geoFencePolygon, String lng, String lat) {
        // 将指定的GPS点转换为坐标
        Coordinate testPoint = parseCoordinate(lng, lat);

        // 创建指定的GPS点
        Point point = geometryFactory.createPoint(testPoint);

        // 检查指定的GPS点是否在电子围栏内
        return geoFencePolygon.contains(point);
    }

    /**
     * 判断指定的GPS点是否在电子围栏内
     *
     * @param fencePointsList 包含电子围栏的经纬度数据的列表，格式为 "经度,纬度"
     * @return 如果在电子围栏内则返回true，否则返回false
     */
    public static Polygon getPointInGeoFence(List<String> fencePointsList) {
        // 将电子围栏的经纬度数据转换为坐标数组
        Coordinate[] fencePoints = parseCoordinates(fencePointsList);
        // 创建电子围栏
        return createPolygon(fencePoints);
    }

    // 地球半径，单位为公里
    private static final double EARTH_RADIUS = 6371.0;

    /**
     * 使用Haversine公式计算两个地理坐标点之间的距离（公里）
     * 
     * @param lat1 第一个点的纬度
     * @param lon1 第一个点的经度
     * @param lat2 第二个点的纬度
     * @param lon2 第二个点的经度
     * @return 两点之间的距离，单位为公里
     */
    public static double haversineDistance(double lat1, double lon1, double lat2, double lon2) {
        // 将角度转换为弧度
        lat1 = Math.toRadians(lat1);
        lon1 = Math.toRadians(lon1);
        lat2 = Math.toRadians(lat2);
        lon2 = Math.toRadians(lon2);

        // 差值
        double dLat = lat2 - lat1;
        double dLon = lon2 - lon1;

        // 应用Haversine公式
        double a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dLon / 2), 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        // 返回距离，单位为公里
        return EARTH_RADIUS * c * 1000;
    }

    /**
     * 根据GPS点集合创建多边形
     *
     * @param coordinates GPS点集合
     * @return 多边形对象
     */
    private static Polygon createPolygon(Coordinate[] coordinates) {
        // Ensure the polygon is closed by adding the first coordinate at the end if necessary
        if (!coordinates[0].equals(coordinates[coordinates.length - 1])) {
            Coordinate[] closedCoordinates = new Coordinate[coordinates.length + 1];
            System.arraycopy(coordinates, 0, closedCoordinates, 0, coordinates.length);
            closedCoordinates[closedCoordinates.length - 1] = coordinates[0];
            coordinates = closedCoordinates;
        }
        return geometryFactory.createPolygon(coordinates);
    }

    /**
     * 将包含经纬度数据的列表转换为坐标数组
     *
     * @param pointsList 包含经纬度数据的列表
     * @return 坐标数组
     */
    private static Coordinate[] parseCoordinates(List<String> pointsList) {
        Coordinate[] coordinates = new Coordinate[pointsList.size()];
        for (int i = 0; i < pointsList.size(); i++) {
            coordinates[i] = parseCoordinate(pointsList.get(i));
        }
        return coordinates;
    }

    /**
     * 将经纬度数据字符串转换为坐标
     *
     * @param pointStr 经纬度数据字符串，格式为 "经度,纬度"
     * @return 坐标
     */
    private static Coordinate parseCoordinate(String pointStr) {
        String[] parts = pointStr.split(",");
        double lon = Double.parseDouble(parts[0]);
        double lat = Double.parseDouble(parts[1]);
        return new Coordinate(lon, lat);
    }

    private static Coordinate parseCoordinate(String lng, String lat) {
        return new Coordinate(Double.parseDouble(lng), Double.parseDouble(lat));
    }
}
