package com.tofly.ghrq_route.common.util.Location;

import com.esri.arcgisruntime.geometry.Point;

import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;

/**
 * Created by Amu on 2017/3/22.
 */

public class LocationUtil {
    private static final double jzA =  6378245.0;
    private static final double jzEE = 0.00669342162296594323;

    private static final double RANGE_LON_MAX =  137.8347;
    private static final double RANGE_LON_MIN =  72.004;
    private static final double RANGE_LAT_MAX =  55.8271;
    private static final double RANGE_LAT_MIN = 0.8293;

    public static final String CURRENT_LOCATION_KEY = "current_location";

    private static double LAT_OFFSET_0(double x, double y){
        return -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(Math.abs(x));
    }

    private static double LAT_OFFSET_1(double x) {
        return (20.0 * sin(6.0 * x * PI) + 20.0 * sin(2.0 * x * PI)) * 2.0 / 3.0;
    }

    private static double LAT_OFFSET_2(double y) {
        return (20.0 * sin(y * PI) + 40.0 * sin(y / 3.0 * PI)) * 2.0 / 3.0;
    }

    private static double LAT_OFFSET_3(double y){
        return (160.0 * sin(y / 12.0 * PI) + 320 * sin(y * PI / 30.0)) * 2.0 / 3.0;
    }

    private static double LON_OFFSET_0(double x, double y){
        return 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x));
    }

    private static double LON_OFFSET_1(double x) {
        return (20.0 * sin(6.0 * x * PI) + 20.0 * sin(2.0 * x * PI)) * 2.0 / 3.0;
    }

    private static double LON_OFFSET_2(double x) {
        return (20.0 * sin(x * PI) + 40.0 * sin(x / 3.0 * PI)) * 2.0 / 3.0;
    }

    private static double LON_OFFSET_3(double x){
        return (150.0 * sin(x / 12.0 * PI) + 300.0 * sin(x / 30.0 * PI)) * 2.0 / 3.0;
    }

    private static double transformLat(double x, double y){
        double ret = LAT_OFFSET_0(x, y);
        ret += LAT_OFFSET_1(x);
        ret += LAT_OFFSET_2(y);
        ret += LAT_OFFSET_3(y);
        return ret;
    }

    private static double transformLon(double x, double y){
        double ret = LON_OFFSET_0(x, y);
        ret += LON_OFFSET_1(x);
        ret += LON_OFFSET_2(x);
        ret += LON_OFFSET_3(x);
        return ret;
    }

    private static boolean outOfChina(double lat, double lon){
        if (lon < RANGE_LON_MIN || lon > RANGE_LON_MAX)
            return true;
        return lat < RANGE_LAT_MIN || lat > RANGE_LAT_MAX;
    }

    private static Point bd09Decrypt(double bdLat, double bdLon){
        double x = bdLon - 0.0065, y = bdLat - 0.006;
        double z = sqrt(x * x + y * y) - 0.00002 * sin(y * PI);
        double theta = Math.atan2(y, x) - 0.000003 * cos(x * PI);
        return new Point(z * cos(theta), z * sin(theta));
    }

    private static Point bd09ToGcj02(Point location){
        return bd09Decrypt(location.getX(), location.getY());
    }

    private static Point gcj02Encrypt(double ggLat, double ggLon){
        double mgLat;
        double mgLon;
        if (outOfChina(ggLat, ggLon)){
            return new Point(ggLon, ggLat);
        }

        double dLat = transformLat(ggLon - 105.0, ggLat - 35.0);
        double dLon = transformLon(ggLon - 105.0, ggLat - 35.0);
        double radLat = ggLat / 180.0 * PI;
        double magic = sin(radLat);
        magic = 1 - jzEE * magic * magic;
        double sqrtMagic = sqrt(magic);
        dLat = (dLat * 180.0) / ((jzA * (1 - jzEE)) / (magic * sqrtMagic) * PI);
        dLon = (dLon * 180.0) / (jzA / sqrtMagic * cos(radLat) * PI);
        mgLat = ggLat + dLat;
        mgLon = ggLon + dLon;

        return new Point(mgLon, mgLat);
    }

    private static Point gcj02Decrypt(double gjLat, double gjLon){
        Point gPt = gcj02Encrypt(gjLat, gjLon);
        double dLon = gPt.getX() - gjLon;
        double dLat = gPt.getY() - gjLat;

        return new Point(gjLon - dLon, gjLat - dLat);
    }

    public static Point bd09ToWgs84(Point baiduLocation){
        Point gcj02 = bd09ToGcj02(baiduLocation);
        return gcj02Decrypt(gcj02.getY(), gcj02.getX());
    }

    /*public static LatLng fromGpsPoint(Point point) {
        CoordinateConverter coordinateConverter = new CoordinateConverter();
        CoordinateConverter from = coordinateConverter.from(CoordinateConverter.CoordType.GPS);
        from.coord(new LatLng(point.getY(), point.getX()));
        return from.convert();
    }*/

    /**
     * 格式化经纬度数据为字符串，保留小数点后6位
     * @param num 输入的经/纬度
     * */
    public static String formatLocation(double num) {
        return String.format("%.6f", num);
    }
}
