package com.ltgk.system.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ltgk.common.constant.CacheConstants;
import com.ltgk.common.core.redis.RedisCache;
import com.ltgk.common.utils.GeoUtils;
import com.ltgk.system.service.DynamicDataService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.stereotype.Service;

import java.awt.geom.Point2D;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.DoubleSummaryStatistics;
import java.util.List;

/**
 * @Author: wan kun
 * @Description: 获取redis缓存中的geo数据 封装函数
 * @Date: 2025/1/9 13:33
 * @Version: 1.0
 */
@Log4j2
@Service
public class DynamicDataServiceImpl implements DynamicDataService {
    @Autowired
    private RedisCache redisCache;

    /**
     * 根据区域获取 redis中 缓存的geo数据个数
     *
     * @param region     不规则以及矩形区域 [{"x":121.27742550292967,"y":28.107271418196024},{"x":121.22867367187499,"y":28.063654504729175}]
     *                   圆形 [{"x":121.14282071490464,"y":28.201514412386377},1534.9207165584596]
     * @param regionType 区域类型 0 为矩形或多边形  1为圆形
     * @return 缓存的geo数据个数
     */
    @Override
    public List<GeoResult<RedisGeoCommands.GeoLocation<String>>> getDynamicCountByRegion(String region, Integer regionType) throws JsonProcessingException {
        if (ObjectUtils.isNull(region)) return null;
        // 区域必须是 不规则以及矩形区域   [{"x":121.27742550292967,"y":28.107271418196024},{"x":121.22867367187499,"y":28.063654504729175}]
        // 圆形 [{"x":121.14282071490464,"y":28.201514412386377},1534.9207165584596]  圆心 半径(米)
        // 如果形状是圆形 直接查询 如果是 矩形需要先找出圆心以及半径 查询 后再根据区域筛选
        Double x = null; // 圆心经度
        Double y = null; // 圆心纬度
        Double radius = null; // 半径 (米)
        if (regionType.equals(0)) {
            // 解析出所有坐标点
            List<Point> points = JSON.parseArray(region, Point.class);
            // 获取最小的经度 最大的纬度坐标点  获取最大的经度 最小的纬度坐标点 获取一个矩形框的左上角 右下角左边 计算圆心以及半径
            DoubleSummaryStatistics xStatistics = points.stream().mapToDouble(Point::getX).summaryStatistics();
            DoubleSummaryStatistics yStatistics = points.stream().mapToDouble(Point::getY).summaryStatistics();
            GeoUtils.CoordinatePoint point1 = new GeoUtils.CoordinatePoint(new BigDecimal(xStatistics.getMin()), new BigDecimal(yStatistics.getMax()));
            GeoUtils.CoordinatePoint point2 = new GeoUtils.CoordinatePoint(new BigDecimal(xStatistics.getMax()), new BigDecimal(yStatistics.getMin()));
            // 根据坐标点计算距离
            GeoUtils.CenterAndDistance centerPoint = GeoUtils.calculateCenterAndDistance(point1, point2);
            y = centerPoint.getCenterPoint().getLatitude().doubleValue();
            x = centerPoint.getCenterPoint().getLongitude().doubleValue();
            radius = centerPoint.getDistance().doubleValue() * 1000;
        } else {
            ObjectMapper objectMapper = new ObjectMapper();
            Object[] objects = objectMapper.readValue(region, Object[].class);
            // 第一个元素是 Map 类型
            if (objects[0] instanceof java.util.Map) {
                java.util.Map<String, Double> map = (java.util.Map<String, Double>) objects[0];
                x = map.get("x");
                y = map.get("y");
            }
            // 第二个元素是 Double 类型
            if (objects[1] instanceof Double) {
                radius = (Double) objects[1];
            }
        }
        // 如果是找不到圆心或半径 直接返回null
        if (ObjectUtils.isNull(x) || ObjectUtils.isNull(y) || ObjectUtils.isNull(radius)) {
            return null;
        }
        Point circlePoint = new Point(x, y);
        // 将自己放入到geo set中 获取到所有的坐标点
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs() // 创建georadius命令参数对象
                .includeDistance() // 包含距离
                .includeCoordinates() // 包含坐标
                .sortAscending(); // 按距离由近及远排序
        Circle circle = new Circle(circlePoint, new Distance(radius, RedisGeoCommands.DistanceUnit.METERS));
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> geoByRegion = redisCache.getGeoByRegion(CacheConstants.TERMINAL_KEY, circle, args);

        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> inRegion = new ArrayList<>();

        // 如果是 矩形或者是其他形状需要判断 是否在区域内 圆形大于矩形
        if (regionType.equals(0)) {
            List<Point2D.Double> pointsNo = new ArrayList<>();
            List<Point> points = JSON.parseArray(region, Point.class);
            points.forEach(point -> {
                pointsNo.add(new Point2D.Double(point.getX(), point.getY()));
            });
            geoByRegion.forEach(geoResult -> {
                // 获取到坐标点
                Point point = geoResult.getContent().getPoint();
                Point2D.Double pointDouble = new Point2D.Double(point.getX(), point.getY());
                // 判断是否在区域内
                // 判断是否在区域内
                if (GeoUtils.isInPolygon(pointDouble, pointsNo)) {
                    inRegion.add(geoResult);
                }
            });
            return inRegion;
        } else {
            return geoByRegion;
        }
    }
}
