package com.mosty.common.util;

import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.linearref.LengthIndexedLine;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

public class JtsUtils {
    private static final double EARTH_RADIUS = 6378137;
    public static GeometryFactory factory = new GeometryFactory(new PrecisionModel(100000000000d));
    private static final DecimalFormat df = new DecimalFormat("0.00000000000");
    private static final DecimalFormat format = new DecimalFormat("0.0000");

    public static String toEwkt(Geometry geom) {
        if (geom == null) {
            return null;
        }
        String type = geom.getGeometryType().toUpperCase();
        switch (type) {
            case "POINT": {
                Coordinate coor = geom.getCoordinate();
                return "POINT(" + formatCoordinate(coor) + ")";
            }
            case "MULTIPOINT": {
                return coordinateToEwkt("MULTIPOINT(", geom.getCoordinates(), ")");
            }
            case "LINESTRING": {
                return coordinateToEwkt("LINESTRING(", geom.getCoordinates(), ")");
            }
            case "POLYGON": {
                return coordinateToEwkt("POLYGON((", geom.getCoordinates(), "))");
            }
            case "MULTILINESTRING": {
                StringBuffer sb = new StringBuffer();
                sb.append("MULTILINESTRING(");
                sb.append(multiGeometryFormat(geom));
                sb.append(")");
                return sb.toString();
            }
            case "MULTIPOLYGON": {
                StringBuffer sb = new StringBuffer();
                sb.append("MULTIPOLYGON(");
                sb.append(multiGeometryFormat(geom));
                sb.append(")");
                return sb.toString();
            }
            case "GEOMETRYCOLLECTION": {
                StringBuffer sb = new StringBuffer();
                int num = geom.getNumGeometries();
                sb.append("GEOMETRYCOLLECTION(");
                for (int j = 0; j < num; j++) {
                    Geometry element = geom.getGeometryN(j);
                    String subType = element.getGeometryType().toUpperCase();
                    if ("POINT".equals(subType)) {
                        sb.append("POINT(" + formatCoordinate(element.getCoordinate()) + ")");
                    } else if ("LINESTRING".equals(subType)) {
                        sb.append(coordinateToEwkt("LINESTRING(", element.getCoordinates(), ")"));
                    } else if ("POLYGON".equals(subType)) {
                        sb.append(coordinateToEwkt("POLYGON((", element.getCoordinates(), "))"));
                    }
                    if (j < num - 1) {
                        sb.append(",");
                    }
                }
                sb.append(")");
                return sb.toString();
            }
        }
        return null;
    }

    private static String formatCoordinate(Coordinate coordinate) {
        return df.format(coordinate.x) + " " + df.format(coordinate.y);
    }

    public static String coordinateToEwkt(String prefix, Coordinate[] coordinates, String suffix) {
        StringBuffer sb = new StringBuffer();
        sb.append(prefix);
        boolean append = false;
        for (Coordinate coordinate : coordinates) {
            if (append) {
                sb.append(",");
            }
            sb.append(formatCoordinate(coordinate));
            append = true;
        }
        sb.append(suffix);
        return sb.toString();
    }

    public static String multiGeometryFormat(Geometry geom) {
        StringBuffer sb = new StringBuffer();
        int num = geom.getNumGeometries();
        for (int i = 0; i < num; i++) {
            Geometry g = geom.getGeometryN(i);
            sb.append(coordinateToEwkt("((", g.getCoordinates(), "))"));
            if (i < num - 1) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

    /**
     * 获取两组成的线的几何中心
     *
     * @param s 起始点
     * @param e 结束点
     * @return
     */
    public static Coordinate getCentroid(Coordinate s, Coordinate e) {// 获取两点组成的线的图心
        if (s.x == e.x && s.y == e.y) {
            return s;
        }
        LineString ls = factory.createLineString(new Coordinate[]{s, e});
        Point cp = ls.getCentroid();
        return cp.getCoordinate();
    }

    /**
     * 获取多个离散几何点的几何中心
     *
     * @param coors
     * @return
     */
    public static Coordinate getCentroid(Coordinate[] coors) {// 获取多个离散点的图心
        MultiPoint mp = factory.createMultiPoint(coors);
        return mp.getCentroid().getCoordinate();
    }

    /**
     * 获取面
     *
     * @param list
     * @return
     */
    public static Polygon getPolygon(List<BigDecimal[]> list) {
        Coordinate[] coors = new Coordinate[list.size()];
        for (int i = 0; i < list.size(); i++) {
            BigDecimal[] item = list.get(i);
            Coordinate coor = new Coordinate();
            coor.x = item[0].doubleValue();
            coor.y = item[1].doubleValue();
            coors[i] = coor;
        }
        return factory.createPolygon(coors);
    }

    /**
     * 获取线
     *
     * @param list [[x,y],[x1,y1],[x2,y2]....]
     * @return
     */
    public static LineString createLine(List<BigDecimal[]> list) {
        Coordinate[] coors = new Coordinate[list.size() - 1];
        for (int i = 0; i < list.size(); i++) {
            BigDecimal[] item = list.get(i);
            Coordinate coor = new Coordinate();
            coor.x = item[0].doubleValue();
            coor.y = item[1].doubleValue();
            coors[i] = coor;
        }
        return factory.createLineString(coors);
    }

    /**
     * 获取点
     *
     * @param x
     * @param y
     * @return
     */
    public static Point getPoint(BigDecimal x, BigDecimal y) {
        Coordinate coor = new Coordinate(x.doubleValue(), y.doubleValue());
        return factory.createPoint(coor);
    }

    // 解析面类型为经纬度数组
    public static List<BigDecimal[]> decodePolygon(Polygon pl) {
        if (pl != null) {
            List<BigDecimal[]> list = new ArrayList<>();
            Coordinate[] coors = pl.getCoordinates();
            if (coors.length > 0) {
                for (Coordinate coor : coors) {
                    BigDecimal[] temp = new BigDecimal[2];
                    temp[0] = new BigDecimal(coor.x);
                    temp[1] = new BigDecimal(coor.y);
                    list.add(temp);
                }
            }
            return list;
        }
        return null;
    }

    public static Coordinate getCenter(LineString line) {
        LengthIndexedLine indexedLine = new LengthIndexedLine(line);
        double[] index = indexedLine.indicesOf(line);
        Coordinate mid = indexedLine.extractPoint((index[0] + index[1]) / 2);
        return mid;
    }
}
