package com.mosty.base.utils;

import com.mosty.common.base.exception.BusinessException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.BuilderException;
import org.locationtech.jts.algorithm.PointLocator;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.operation.buffer.BufferOp;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 其他的一些工具类
 */
public class Kit {
    /**
     * 对象转Map
     *
     * @param obj
     * @return
     * @throws IllegalAccessException
     */
    public static Map beanToMap(Object obj) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String value = field.get(obj) != null ? field.get(obj).toString() : "";
            map.put(field.getName(), value);
        }
        return map;
    }

    /**
     * 文件转base64
     *
     * @param file
     * @return
     */
    public static String fileToBase64(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("图片不能为空!");
        }
        // String fileName = file.getOriginalFilename();
        // String fileType = fileName.substring(fileName.lastIndexOf("."));
        String contentType = file.getContentType();
        byte[] imageBytes;
        String base64EncoderImg;
        try {
            imageBytes = file.getBytes();
            BASE64Encoder base64Encoder = new BASE64Encoder();
//            base64EncoderImg = "data:" + contentType + ";base64," + base64Encoder.encode(imageBytes);
            base64EncoderImg = base64Encoder.encode(imageBytes);
            base64EncoderImg = base64EncoderImg.replaceAll("[\\s*\t\n\r]", "");
        } catch (IOException e) {
            e.printStackTrace();
            throw new BuilderException("图片转base64异常");
        }
        return base64EncoderImg;
    }


    /**
     * 取得两个经纬度之间的距离<br>
     *
     * @param longitude1 坐标点1经度
     * @param latitude1  坐标点1纬度
     * @param longitude2 坐标点2经度
     * @param latitude2  坐标点2纬度
     * @return
     */
    public static Double getDistance(double longitude1, double latitude1,
                                     double longitude2, double latitude2) {
        double distance = 0;
        if (longitude1 != 0 && latitude1 != 0 && longitude2 != 0
                && latitude2 != 0) {
            double radius = 6378140; // 假设地球为正球形，直径为6378140米
            double PI = Math.PI;
            distance = (2 * Math.atan2(Math
                    .sqrt(Math.sin((latitude1 - latitude2) * PI / 180
                            / 2)
                            * Math.sin((latitude1 - latitude2) * PI
                            / 180 / 2)
                            + Math.cos(latitude2 * PI / 180)
                            * Math.cos(latitude1 * PI / 180)
                            * Math.sin((longitude1 - longitude2) * PI
                            / 180 / 2)
                            * Math.sin((longitude1 - longitude2) * PI
                            / 180 / 2)), Math.sqrt(1
                    - Math
                    .sin((latitude1 - latitude2) * PI / 180 / 2)
                    * Math
                    .sin((latitude1 - latitude2) * PI / 180 / 2)
                    + Math.cos(latitude2 * PI / 180)
                    * Math.cos(latitude1 * PI / 180)
                    * Math.sin((longitude1 - longitude2) * PI / 180
                    / 2)
                    * Math.sin((longitude1 - longitude2) * PI / 180
                    / 2))))
                    * radius;
            distance = Math.round(distance * 100); // 取两位小数
            distance = distance / 100;
        }
        return distance;
    }

    // unicode编码转中文
    public static String decodeUnicode(String dataStr) {
        int start = 0;
        int end = 0;
        StringBuilder buffer = new StringBuilder();
        while (start > -1) {
            end = dataStr.indexOf("\\x", start + 2);
            String charStr = "";
            if (end == -1) {
                charStr = dataStr.substring(start + 2, dataStr.length());
            } else {
                charStr = dataStr.substring(start + 2, end);
            }
            char letter = (char) Integer.parseInt(charStr, 16); // 16进制parse整形字符串。
            buffer.append(letter);
            start = end;
        }
        return buffer.toString();
    }


    // 将字符串中的unicode编码转换成中文
    public static String unicodeToStr(String str) {
        if (StringUtils.isBlank(str)) {
            return str;
        }
        Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
        Matcher matcher = pattern.matcher(str);
        char ch;
        while (matcher.find()) {
            ch = (char) Integer.parseInt(matcher.group(2), 16);
            str = str.replace(matcher.group(1), ch + "");
        }
        return str;
    }

    /**
     * 判断点在线的范围内（缓冲区法）
     * @param coordinates 线坐标
     * @param isPoint 点位
     * @return 是否在范围内
     */
    public static boolean isInLineGeometry(Coordinate[] coordinates, String isPoint) {
        String[] sp = isPoint.split(",");
        double xx = Double.parseDouble(sp[0]);
        double yy = Double.parseDouble(sp[1]);
        //点是否在多边形内判断
        Coordinate point = new Coordinate(xx, yy);
        PointLocator a = new PointLocator();
        GeometryFactory gf = new GeometryFactory();
        Geometry gfLineString = gf.createLineString(coordinates);
        double degree = 500 / (2 * Math.PI * 6371004) * 360;
        BufferOp bufOp = new BufferOp(gfLineString);
        bufOp.setEndCapStyle(BufferOp.CAP_BUTT);
        Geometry bg = bufOp.getResultGeometry(degree);
        return a.intersects(point, bg);
    }

    /**
     * 判断点在范围内（缓冲区法）（不推荐）
     * @param coordinates 范围边界
     * @param isPoint 点位
     * @return 是否在范围内
     */
    public static boolean isInGeometry(Coordinate[] coordinates, String isPoint) {
        if (coordinates[0].x != coordinates[coordinates.length - 1].x || coordinates[0].y != coordinates[coordinates.length - 1].y) {
            coordinates[coordinates.length - 1] = new Coordinate(coordinates[0].x, coordinates[0].y);
        }
        String[] sp = isPoint.split(",");
        double xx = Double.parseDouble(sp[0]);
        double yy = Double.parseDouble(sp[1]);
        //点是否在多边形内判断
        Coordinate point = new Coordinate(xx, yy);
        PointLocator a = new PointLocator();
        GeometryFactory gf = new GeometryFactory();
        Geometry gfLineString = gf.createPolygon(coordinates);
        double degree = 500 / (2 * Math.PI * 6371004) * 360;
        BufferOp bufOp = new BufferOp(gfLineString);
        bufOp.setEndCapStyle(BufferOp.CAP_BUTT);
        Geometry bg = bufOp.getResultGeometry(degree);
        return a.intersects(point, bg);
    }

    /**
     * 判断点在范围内（射线法）（推荐）
     * @param coordinates 范围边界
     * @param isPoint 点位
     * @return 是否在范围内
     */
    public static boolean isInGeometryByRay(Coordinate[] coordinates, String isPoint){
        if(StringUtils.isBlank(isPoint) || coordinates == null || coordinates.length <= 0){
            return false;
        }

        int count = 0;
        int length = coordinates.length;

        String[] sp = isPoint.split(",");
        double pointX = Double.parseDouble(sp[0]);
        double pointY = Double.parseDouble(sp[1]);

        for(int i = 0 ; i < length ; i++){
            Coordinate c1 = coordinates[i];
            Coordinate c2 = coordinates[(i + 1) % length];
            if(c1.x == pointX && c1.y == pointY){
                return true;
            }
            if(c1.y == c2.y){
                continue;
            }
            if(pointY < Math.min(c1.y, c2.y)){
                continue;
            }
            if(pointY >= Math.max(c1.y, c2.y)){
                continue;
            }
            double x = (pointY - c1.y) * (c2.x - c1.x) / (c2.y - c1.y) + c1.x;
            if(x > pointX){
                count++;
            }else if(x == pointX){
                return true;
            }
        }

        return count % 2 == 1;
    }

    // 检查经纬度是否合法
    public static boolean checkJwd(BigDecimal jd, BigDecimal wd) {
        if (jd == null || wd == null) return true;
        if (jd.doubleValue() > 180L || jd.doubleValue() < -180L) {
            return false;
        }
        return !(wd.doubleValue() > 90L) && !(wd.doubleValue() < -90L);
    }

    // 判断字符串是否为数值
    public static boolean isNumber(String str) {
        for (int i = 0; i < str.length(); i++) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    // 解析面类型为经纬度数组
    public static List<BigDecimal[]> decodePolygon(String pl) {
        if (ObjectUtils.isNotEmpty(pl)) {
            List<BigDecimal[]> list = new ArrayList<>();
            List<String> coors = Arrays.asList(pl.split(","));
            for (int i = 0; i < coors.size(); i = i + 2) {
                BigDecimal[] temp = new BigDecimal[2];
                temp[0] = new BigDecimal(coors.get(i));
                temp[1] = new BigDecimal(coors.get(i + 1));
                list.add(temp);
            }
            return list;
        }
        return null;
    }

    public static void main(String[] args) {

        String qy = "104.72283588722,31.51256561279,104.72484217957,31.5102159977,104.72811447456,31.50651454926,104.7266660817,31.50549530983,104.72650514916,31.50521636009,104.7263978608,31.50503396988,104.72636567429,31.50476574898,104.7270845063,31.50304913521,104.72724543884,31.50257706642,104.72740637138,31.50194406509,104.72771750763,31.49861812592,104.72804758698,31.49434804916,104.72813341767,31.49299621582,104.72907755524,31.48911237717,104.72954962403,31.48640871048,104.73090522923,31.48658037186,104.73222487606,31.48667156696,104.74315378815,31.487159729,104.7430919297,31.48764252663,104.74313484505,31.488468647,104.74319921806,31.48914456367,104.74175903946,31.49587154388,104.74122259766,31.4987039566,104.74095689133,31.49990558624,104.74064575508,31.50073170662,104.73997805268,31.50192260742,104.73699040711,31.50544166565,104.73321385682,31.51020526886,104.73123975098,31.51200771332,104.72544617951,31.51453971863,104.72283588722,31.51256561279";
        List<BigDecimal[]> bigDecimals = decodePolygon(qy);
        Coordinate[] coordinates = new Coordinate[bigDecimals.size()];
        for (int i = 0; i < bigDecimals.size(); i++) {
            BigDecimal[] jwd = bigDecimals.get(i);
            Coordinate coor = new Coordinate();
            coor.x = jwd[0].doubleValue();
            coor.y = jwd[1].doubleValue();
            coordinates[i] = coor;
        }
        System.out.println(Kit.isInGeometry(coordinates, 104.72283588722 + "," + 31.51256561279));
    }
}
