package com.ssj.service.conch.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ssj.service.conch.conch.request.LocationReqNew;
import com.ssj.service.conch.conch.request.LocationRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Mount Liu
 * @date 2018/12/12
 */
public class MapUtils {
    private static final Logger logger = LoggerFactory.getLogger(MapUtils.class);
    /**
     * WGS84（国际坐标）
     * GCJ-02（火星坐标）：高德
     * BD-09（百度坐标）：百度
     */
    static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    /**
     * 百度地图web服务
     */
    private static final String BAIDU_WEB_KEY = "X3utfT7g5IuuZ4yoBNEzENxnVykPyVHq";
    /**
     * 高德地图web服务
     */
    private static final String AMPA_WEB_KEY = "3b8ed816b7ba03abda3e27d3f1c471de";

    private static final double EARTH_RADIUS = 6371393; // 平均半径,单位：m

    /**
     * 高德转百度
     * @param gd_lon 经度
     * @param gd_lat 纬度
     * @return
     */
    public static Map<String, Object> gdToBdEncrypt(double gd_lon, double gd_lat) {
        Map<String, Object> data = new HashMap<>(16);
        double x = gd_lon, y = gd_lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double bd_lon = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        data.put("lon", bd_lon);
        data.put("lat", bd_lat);
        return data;
    }

    /**
     * 百度转高德
     *
     * @param bd_lon 经度
     * @param bd_lat 纬度
     * @return
     */
    public static Map<String, Double> bdToGdEncrypt(double bd_lon, double bd_lat) {
        double x = bd_lon - 0.0065, y = bd_lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double gd_lon = z * Math.cos(theta);
        double gd_lat = z * Math.sin(theta);
        Map<String, Double> data = new HashMap<>(16);
        data.put("lon", gd_lon);
        data.put("lat", gd_lat);
        return data;
    }

    /**
     * 通过百度地图API转换其他坐标系经纬度
     * @param gd_lon
     * @param gd_lat
     * @param from
    1：GPS设备获取的角度坐标，WGS84坐标;
    2：GPS获取的米制坐标、sogou地图所用坐标;
    3：google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标，国测局（GCJ02）坐标;
    4：3中列表地图坐标对应的米制坐标;
    5：百度地图采用的经纬度坐标;
    6：百度地图采用的米制坐标;
    7：mapbar地图坐标;
    8：51地图坐标
     * @return
     */
    public static Map<String, Object> geoconvByBdMapApi(double gd_lon, double gd_lat, int from) {
        Map<String, Object> map = new HashMap<>(16);
        Map<String, Object> param = new HashMap<>(16);
        param.put("ak", BAIDU_WEB_KEY);
        param.put("coords", gd_lon + "," + gd_lat);
        param.put("from", from);
        String url = "http://api.map.baidu.com/geoconv/v1/?coords={coords}&from={from}&to=5&ak={ak}";
        RestTemplate restTemplateOuter = new RestTemplate();
        restTemplateOuter.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        String rsp = restTemplateOuter.getForObject(url, String.class, param);
        JSONObject jsonObject = JSON.parseObject(rsp);
        String status = jsonObject.getString("status");
        if ("0".equals(status)) {
            JSONArray result = jsonObject.getJSONArray("result");
            String location = result.getString(0);
            JSONObject resultObject = JSON.parseObject(location);
            map.put("lon", resultObject.getString("x"));
            map.put("lat", resultObject.getString("y"));
        } else {
            logger.error("百度地图返回报文错误信息："+rsp);
        }
        return map;
    }

    /**
     * 通过高德地图API-转换其他坐标系经纬度为高德坐标系
     * @param lon
     * @param lat
     * @param coordsys 原坐标系（可选值： gps; mapbar; baidu; autonavi(不进行转换)）
     * @return
     */
    public static Map<String, Object> geoconvByAmapApi(double lon, double lat, String coordsys) {
        Map<String, Object> map = new HashMap<>(16);
        Map<String, Object> param = new HashMap<>(16);
        param.put("key", AMPA_WEB_KEY);
        param.put("locations", lon + "," + lat);
        param.put("coordsys", coordsys);
        String url = "https://restapi.amap.com/v3/assistant/coordinate/convert?key={key}&locations={locations}&coordsys={coordsys}";
        RestTemplate restTemplateOuter = new RestTemplate();
        restTemplateOuter.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        String rsp = restTemplateOuter.getForObject(url, String.class, param);
        JSONObject jsonObject = JSON.parseObject(rsp);
        String status = jsonObject.getString("status");
        if ("1".equals(status)) {
            String locations = jsonObject.getString("locations");
            String[] locationArr = locations.split(",");
            map.put("lon", locationArr[0]);
            map.put("lat", locationArr[1]);
        } else {
            logger.error("高德地图返回报文错误信息："+rsp);
        }
        return map;
    }

    /**
     * 基于临近基站和WIFI热点的高精度混合基站定位
     * @param req
     * cl 临近基站列表，基站之间以分号分隔，每条基站信息对应5个参数，以逗号分隔，分别为MCC,MNC,LAC,CI,信号强度，其中信号强度的单位是dBm
     * wl 临近WIFI热点列表，WIFI热点之间以分号分隔，每条WIFI热点信息对应两个参数，分别为MAC地址和信号强度
     * coord 坐标类型(wgs84/gcj02/bd09)，默认wgs84
     * output 返回格式(csv/json/xml)，默认csv
     * @return
     */
    public static Map<String, Object> lbsAndWifiLocate(LocationRequest req) {
        Map<String, Object> map = new HashMap<>(16);
        RestTemplate restTemplateOuter = new RestTemplate();
        restTemplateOuter.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        String wl = "";
        if(StringUtils.isNotBlank(req.getBssid1())){
            if(wl == ""){
                wl += (req.getBssid1()+","+req.getdBm1());
            }else {
                wl += (";"+req.getBssid1()+","+req.getdBm1());
            }
        }
        if(StringUtils.isNotBlank(req.getBssid2())){
            if(wl == ""){
                wl += (req.getBssid2()+","+req.getdBm2());
            }else {
                wl += (";"+req.getBssid2()+","+req.getdBm2());
            }
        }
        if(StringUtils.isNotBlank(req.getBssid3())){
            if(wl == ""){
                wl += (req.getBssid3()+","+req.getdBm3());
            }else {
                wl += (";"+req.getBssid3()+","+req.getdBm3());
            }
        }
        if(StringUtils.isNotBlank(req.getBssid4())){
            if(wl == ""){
                wl += (req.getBssid4()+","+req.getdBm4());
            }else {
                wl += (";"+req.getBssid4()+","+req.getdBm4());
            }
        }
        if(StringUtils.isNotBlank(req.getBssid5())){
            if(wl == ""){
                wl += (req.getBssid5()+","+req.getdBm5());
            }else {
                wl += (";"+req.getBssid5()+","+req.getdBm5());
            }
        }
        //MCC,MNC,LAC,CI
        String cl = "";
        if (req.getMnc1() != 0 || req.getLac1() != 0 || req.getCell1() != 0 || req.getStrength1() != 0) {
            if (cl == "") {
                cl += "460," + req.getMnc1() + "," + req.getLac1() + "," + req.getCell1() + "," + req.getStrength1();
            } else {
                cl += ";460," + req.getMnc1() + "," + req.getLac1() + "," + req.getCell1() + "," + req.getStrength1();
            }
        }
        if (req.getMnc2() != 0 || req.getLac2() != 0 || req.getCell2() != 0 || req.getStrength2() != 0) {
            if (cl == "") {
                cl += "460," + req.getMnc2() + "," + req.getLac2() + "," + req.getCell2() + "," + req.getStrength2();
            } else {
                cl += ";460," + req.getMnc2() + "," + req.getLac2() + "," + req.getCell2() + "," + req.getStrength2();
            }
        }
        if (req.getMnc3() != 0 || req.getLac3() != 0 || req.getCell3() != 0 || req.getStrength3() != 0) {
            if (cl == "") {
                cl += "460," + req.getMnc3() + "," + req.getLac3() + "," + req.getCell3() + "," + req.getStrength3();
            } else {
                cl += ";460," + req.getMnc3() + "," + req.getLac3() + "," + req.getCell3() + "," + req.getStrength3();
            }
        }
        if (req.getMnc4() != 0 || req.getLac4() != 0 || req.getCell4() != 0 || req.getStrength4() != 0) {
            if (cl == "") {
                cl += "460," + req.getMnc4() + "," + req.getLac4() + "," + req.getCell4() + "," + req.getStrength4();
            } else {
                cl += ";460," + req.getMnc4() + "," + req.getLac4() + "," + req.getCell4() + "," + req.getStrength4();
            }
        }
        String url = "http://api.cellocation.com:81/loc/?&output=json&coord=bd09";
        if(wl != ""){
            url += ("&wl=" + wl);
        }
        if(cl != ""){
            url += "&cl="+cl;
        }
        logger.info("url========="+url);
        try{
            String rsp = restTemplateOuter.getForObject(url, String.class);
            logger.info("基于临近基站和WIFI热点的高精度混合基站定位API返回信息："+rsp);
            JSONObject jsonObject = JSON.parseObject(rsp);
            Integer status = jsonObject.getInteger("errcode");
            //成功
            if (0 == status) {
                map.put("lon", jsonObject.getString("lon"));
                map.put("lat", jsonObject.getString("lat"));
                map.put("address", jsonObject.getString("address"));
            } else if (1000 == status){
                map.put("errorMsg","参数错误");
            } else if (1001 == status){
                map.put("errorMsg","无查询结果");
            }
        }catch (Exception e){
            map.put("errorMsg","接口调用已达上限");
            logger.error("基于临近基站和WIFI热点的高精度混合基站定位API返回信息异常："+e);
        }
        return map;
    }

    /**
     * 高德地图基站+wifi混合定位
     * @param record
     * @return
     */
    public static Map<String,Object> amapLocate(LocationReqNew record){
        Map<String, Object> map = new HashMap<>(16);
        RestTemplate restTemplateOuter = new RestTemplate();
        restTemplateOuter.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        String url = "http://apilocate.amap.com/position?key=8753f9139826a00b09baa6c1f786c08d" +
                "&accesstype="+ record.getAccesstype()+
                "&imei=" + record.getImei()+
                "&cdma=" + record.getCdma();
        if(StringUtils.isNotEmpty(record.getSmac())){
            url += "&smac="+record.getSmac();
        }
        if(StringUtils.isNotEmpty(record.getServerip())){
            url += "&serverip="+record.getServerip();
        }
        if(StringUtils.isNotEmpty(record.getImsi())){
            url += "&imsi="+record.getImsi();
        }
        if(StringUtils.isNotEmpty(record.getNetwork())){
            url += "&network="+record.getNetwork();
        }
        if(StringUtils.isNotEmpty(record.getBts())){
            url += "&bts="+record.getBts();
        }
        if(StringUtils.isNotEmpty(record.getNearbts())){
            url += "&nearbts="+record.getNearbts();
        }
        if(StringUtils.isNotEmpty(record.getMmac())){
            url += "&mmac="+record.getMmac();
        }
        if(StringUtils.isNotEmpty(record.getMacs())){
            url += "&macs="+record.getMacs();
        }
        logger.info("高德API URL："+url);
        String rsp = restTemplateOuter.getForObject(url, String.class);
        logger.info("高德API返回信息："+rsp);
        JSONObject jsonObject = JSON.parseObject(rsp);
        String status = jsonObject.getString("status");
        if ("1".equals(status)) {
            String result = jsonObject.getString("result");
            JSONObject resultObject = JSON.parseObject(result);
            String type = resultObject.getString("type");
            if (!"0".equals(type)) {
                String loction = resultObject.getString("location");
                String[] gps = loction.split(",");
                //高德经纬度转为为百度坐标
                //Map baiduMap = geoconvByBdMapApi(Double.valueOf(gps[0]), Double.valueOf(gps[1]),3);
                //logger.info("转换为的百度坐标："+baiduMap.get("lon")+","+baiduMap.get("lat"));
                map.put("lon", gps[0]);
                map.put("lat", gps[1]);
                map.put("address", resultObject.getString("desc"));
            }
        }
        return map;
    }

    public static JSONObject getLngAndLat(String address){
        //OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77
        String key = "CA5BZ-KRF3J-3RXFX-FUAYV-EUIC7-ADBDK";
        String url = "https://apis.map.qq.com/ws/geocoder/v1/?key="+key+"&address=" + address;
        RestTemplate restTemplateOuter = new RestTemplate();
        restTemplateOuter.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        String rsp = restTemplateOuter.getForObject(url, String.class);
        JSONObject jsonObject = JSON.parseObject(rsp);
        String status = jsonObject.getString("status");
        if ("0".equals(status)) {
            JSONObject location = jsonObject.getJSONObject("result").getJSONObject("location");
            return location;
        } else {
            logger.error("地图返回报文错误信息：" + rsp);
        }
        return null;
    }

    public static void main(String[] args) {
        /*Map map = geoconvByAmapApi(116.481499,39.990475,"gps");
        System.out.println(map.get("lon")+","+map.get("lat"));*/
        Map map = regeoByAmapApi(113.340431,23.150356);
        System.out.println(map);
    }

    /**
     * 通过两点经纬度获取距离
     * @param lon1
     * @param lat1
     * @param lon2
     * @param lat2
     * @return 距离(单位：千米)
     */
    public static double getDistance(double lon1, double lat1, double lon2, double lat2) {
        double radiansAX = Math.toRadians(lon1);
        double radiansAY = Math.toRadians(lat1);
        double radiansBX = Math.toRadians(lon2);
        double radiansBY = Math.toRadians(lat2);
        double cos = Math.cos(radiansAY) * Math.cos(radiansBY) * Math.cos(radiansAX - radiansBX)
                + Math.sin(radiansAY) * Math.sin(radiansBY);
        //反余弦值
        double acos = Math.acos(cos);
        double distance = EARTH_RADIUS * acos;
        BigDecimal b = new BigDecimal(distance/1000);
        distance = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        return distance;
    }

    /**
     * 经纬度转换为具体地址
     * @param lon
     * @param lat
     * @return
     */
    public static Map<String, Object> regeoByAmapApi(double lon, double lat) {
        Map<String, Object> map = new HashMap<>(16);
        Map<String, Object> param = new HashMap<>(16);
        param.put("key", AMPA_WEB_KEY);
        param.put("location", lon + "," + lat);
        String url="https://restapi.amap.com/v3/geocode/regeo?key={key}&location={location}";
        RestTemplate restTemplateOuter = new RestTemplate();
        restTemplateOuter.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        String rsp = restTemplateOuter.getForObject(url, String.class, param);
        JSONObject jsonObject = JSON.parseObject(rsp);
        System.out.println(jsonObject);
        String status = jsonObject.getString("status");
        if ("1".equals(status)) {
            JSONObject regeocode = jsonObject.getJSONObject("regeocode");
            map.put("address", regeocode.getString("formatted_address"));
            JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
            //楼信息列表
            JSONObject building = addressComponent.getJSONObject("building");
            map.put("building", building.getString("name").replace("[]",""));
            //门牌信息列表
            JSONObject streetNumber = addressComponent.getJSONObject("streetNumber");
            map.put("streetNumber", (streetNumber.getString("street")+streetNumber.getString("number")).replace("[]",""));
            //社区信息列表
            JSONObject neighborhood = addressComponent.getJSONObject("neighborhood");
            map.put("neighborhood", neighborhood.getString("name").replace("[]",""));
        } else {
            logger.error("高德地图返回报文错误信息："+rsp);
        }
        return map;
    }

}