/*
 * Copyright (c) 2016-present, lovebing.net.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package org.lovebing.reactnative.baidumap.module;

import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.location.LocationClientOption.LocationMode;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.search.core.PoiInfo;
import com.baidu.mapapi.search.core.SearchResult;
import com.baidu.mapapi.search.geocode.GeoCodeOption;
import com.baidu.mapapi.search.geocode.GeoCodeResult;
import com.baidu.mapapi.search.geocode.GeoCoder;
import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult;
import com.baidu.mapapi.utils.CoordinateConverter;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;

import org.lovebing.reactnative.baidumap.R;
import org.lovebing.reactnative.baidumap.support.AppUtils;

import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.util.Log;

import androidx.core.app.NotificationCompat;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import static android.content.Context.NOTIFICATION_SERVICE;

/**
 * Created by lovebing on 2016/10/28.
 */
public class GeolocationModule extends BaseModule
        implements BDLocationListener, OnGetGeoCoderResultListener {

    private LocationClient locationClient;
    private LocationClient locationClient2;

    private static GeoCoder geoCoder;
    private volatile boolean locating = false;
    private volatile boolean locateOnce = false;

    private final BDAbstractLocationListener bdListener;
    private final BDAbstractLocationListener bdListener2;
    private java.util.Timer timer;
//    private TimerTask timerTask = new TimerTask() {
//        @Override
//        public void run() {
//            if(locationClient!=null) {
//                if(locationClient.isStarted())
//                    locationClient.requestLocation();
//                else {
//                    locationClient.start();
//                }
//            }
//        }
//    };

    public GeolocationModule(ReactApplicationContext reactContext) {
        super(reactContext);
        context = reactContext;
        bdListener  = new BDAbstractLocationListener() {
            @Override
            public void onReceiveLocation(BDLocation bdLocation) {
                Log.i("locationClient", "onLocationUpdate");

                WritableMap params =   GeolocationModule.this.onLocation(bdLocation);

                sendEvent("onLocationUpdate", params);
            }
        };

        bdListener2  = new BDAbstractLocationListener() {
            @Override
            public void onReceiveLocation(BDLocation bdLocation) {
                Log.i("locationClient", "onGetCurrentLocationPosition");

                WritableMap params =   GeolocationModule.this.onLocation(bdLocation);
                sendEvent("onGetCurrentLocationPosition", params);
                //locationClient2.stop();
                //locationClient2 = null;

            }
        };
    }




    public String getName() {
        return "BaiduGeolocationModule";
    }

    private void initLocationClient(String coorType, boolean locateOnce) {
        if (context.getCurrentActivity() != null) {
            AppUtils.checkPermission(context.getCurrentActivity(), Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if (locationClient != null) return;
        LocationClientOption option = new LocationClientOption();
        option.setLocationMode(LocationMode.Hight_Accuracy);
        option.setCoorType(coorType);
        option.setIsNeedAddress(true);
        option.setIsNeedAltitude(true);
        option.setIsNeedLocationDescribe(true);
        option.setOpenGps(true);
        option.setScanSpan(20*1000);
        option.setWifiCacheTimeOut(20000);
        option.setOnceLocation(false);
        option.setIgnoreKillProcess(false);
        option.setOpenAutoNotifyMode();
        locationClient = new LocationClient(context.getApplicationContext());
        locationClient.setLocOption(option);
        Log.i("locationClient", "locationClient");
        locationClient.registerLocationListener(bdListener);
        if (!locateOnce) {

            Intent clickIntent = new Intent();
            clickIntent.setAction("com.biz.app.click");
            //添加其他数据
            clickIntent.putExtra("message key", "message");//将message放入intent中，方便通知自建通知的点击事件

            Notification notification = new NotificationCompat.Builder(context, "1003")
                    .setContentTitle("正在进行后台定位") // 设置下拉列表里的标题
                    .setSmallIcon(R.mipmap.icon_gcoding) // 设置状态栏内的小图标
                    .setContentText("后台定位通知") // 设置上下文内容
                    .setAutoCancel(true)
                    .setWhen(System.currentTimeMillis())
                    .setDefaults(Notification.DEFAULT_VIBRATE)
                    .setPriority(Notification.PRIORITY_DEFAULT)
                    .build();
            notification.contentIntent = PendingIntent.getService(context, 1000, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            locationClient.enableLocInForeground(1, notification);// 调起前台定位

        }
    }

    /**
     * @return
     */
    protected GeoCoder getGeoCoder() {
        if (geoCoder != null) {
            geoCoder.destroy();
        }
        geoCoder = GeoCoder.newInstance();
        geoCoder.setOnGetGeoCodeResultListener(this);
        return geoCoder;
    }

    /**
     * @param sourceLatLng
     * @return
     */
    protected LatLng getBaiduCoorFromGPSCoor(LatLng sourceLatLng) {
        CoordinateConverter converter = new CoordinateConverter();
        converter.from(CoordinateConverter.CoordType.GPS);
        converter.coord(sourceLatLng);
        LatLng desLatLng = converter.convert();
        return desLatLng;

    }

    @ReactMethod
    public void convertGPSCoor(double lat, double lng, Promise promise) {
        Log.i("convertGPSCoor", "convertGPSCoor");
        LatLng latLng = getBaiduCoorFromGPSCoor(new LatLng(lat, lng));
        WritableMap map = Arguments.createMap();
        map.putDouble("latitude", latLng.latitude);
        map.putDouble("longitude", latLng.longitude);
        promise.resolve(map);
    }

    @ReactMethod
    public void getCurrentPosition(String coorType) {
//        if (locating) {
//            return;
//        }
        if (locationClient2 == null) {
            //initLocationClient(coorType, true);
            LocationClientOption option = new LocationClientOption();
            option.setLocationMode(LocationMode.Hight_Accuracy);
            option.setCoorType(coorType);
            option.setIsNeedAddress(true);
            option.setIsNeedAltitude(true);
            option.setIsNeedLocationDescribe(true);
            option.setOpenGps(true);
            option.setScanSpan(0);
            option.setWifiCacheTimeOut(2000);
            option.setOnceLocation(true);
            option.setIgnoreKillProcess(false);
            option.setOpenAutoNotifyMode();
            locationClient2 = new LocationClient(context.getApplicationContext());
            locationClient2.setLocOption(option);
            Log.i("locationClient", "locationClient");
            locationClient2.registerLocationListener(bdListener2);
        }
        Log.i("getCurrentPosition", "getCurrentPosition");

        locationClient2.start();
        locationClient2.requestLocation();
    }

    @ReactMethod
    public void startLocating(String coorType) {
        if (locating) {
            return;
        }
        if(timer==null){
            timer = new Timer();
        }
        locateOnce = false;
        locating = true;
        initLocationClient(coorType, false);
        locationClient.start();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if(locationClient!=null) {
                    if(locationClient.isStarted())
                        locationClient.requestLocation();
                    else {
                        locationClient.start();
                    }
                }
            }
        }, 10 * 1000, 10 * 1000);
    }

    @ReactMethod
    public void stopLocating() {
        Log.i("locationClient", "stopLocating");

        locating = false;
        if (locationClient != null ) {


            locationClient.disableLocInForeground(true);
            locationClient.unRegisterLocationListener(bdListener);
            locationClient.stop();
            locationClient = null;
            locationClient2 = null;
            try{
                NotificationManager manager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
                manager.cancel(1);
                if(timer!=null) {
                    timer.cancel();
                    timer = null;
                }
            }catch (Exception e){}catch (Error e){}
        }
    }

    @ReactMethod
    public void geocode(String city, String addr) {
        getGeoCoder().geocode(new GeoCodeOption()
                .city(city).address(addr));
    }

    @ReactMethod
    public void reverseGeoCode(double lat, double lng) {
        getGeoCoder().reverseGeoCode(new ReverseGeoCodeOption()
                .location(new LatLng(lat, lng)));
    }

    @ReactMethod
    public void reverseGeoCodeGPS(double lat, double lng) {
        getGeoCoder().reverseGeoCode(new ReverseGeoCodeOption()
                .location(getBaiduCoorFromGPSCoor(new LatLng(lat, lng))));
    }

    WritableMap onLocation(BDLocation bdLocation){
        WritableMap params = Arguments.createMap();
        params.putDouble("latitude", bdLocation.getLatitude());
        params.putDouble("longitude", bdLocation.getLongitude());
        params.putDouble("speed", bdLocation.getSpeed());
        params.putDouble("direction", bdLocation.getDirection());
        params.putDouble("altitude", bdLocation.getAltitude());
        params.putDouble("radius", bdLocation.getRadius());
        params.putString("address", bdLocation.getAddrStr());
        params.putString("countryCode", bdLocation.getCountryCode());
        params.putString("country", bdLocation.getCountry());
        params.putString("province", bdLocation.getProvince());
        params.putString("cityCode", bdLocation.getCityCode());
        params.putString("city", bdLocation.getCity());
        params.putString("district", bdLocation.getDistrict());
        params.putString("street", bdLocation.getStreet());
        params.putString("streetNumber", bdLocation.getStreetNumber());
        params.putString("buildingId", bdLocation.getBuildingID());
        params.putString("buildingName", bdLocation.getBuildingName());
        Log.i("onReceiveLocation", "onGetCurrentLocationPosition");
        return params;
    }

    @Override
    public void onReceiveLocation(BDLocation bdLocation) {
        WritableMap params = onLocation(bdLocation);
        Log.i("onReceiveLocation", "onGetCurrentLocationPosition");

        if (locateOnce) {
            locating = false;
            sendEvent("onGetCurrentLocationPosition", params);
            //locationClient.stop();
            //locationClient = null;
        } else {
            sendEvent("onLocationUpdate", params);
        }
    }

    @Override
    public void onGetGeoCodeResult(GeoCodeResult result) {
        WritableMap params = Arguments.createMap();
        if (result == null || result.error != SearchResult.ERRORNO.NO_ERROR) {
            params.putInt("errcode", -1);
            params.putString("errmsg", result.error.name());
        } else {
            params.putDouble("latitude", result.getLocation().latitude);
            params.putDouble("longitude", result.getLocation().longitude);
        }
        sendEvent("onGetGeoCodeResult", params);
    }

    @Override
    public void onGetReverseGeoCodeResult(ReverseGeoCodeResult result) {
        WritableMap params = Arguments.createMap();
        if (result == null || result.error != SearchResult.ERRORNO.NO_ERROR) {
            params.putInt("errcode", -1);
        } else {
            ReverseGeoCodeResult.AddressComponent addressComponent = result.getAddressDetail();
            params.putString("address", result.getAddress());
            params.putString("province", addressComponent.province);
            params.putString("city", addressComponent.city);
            params.putString("district", addressComponent.district);
            params.putString("street", addressComponent.street);
            params.putString("streetNumber", addressComponent.streetNumber);

            WritableArray list = Arguments.createArray();
            List<PoiInfo> poiList = result.getPoiList();
            for (PoiInfo info : poiList) {
                WritableMap attr = Arguments.createMap();
                attr.putString("name", info.name);
                attr.putString("address", info.address);
                attr.putString("city", info.city);
                attr.putDouble("latitude", info.location.latitude);
                attr.putDouble("longitude", info.location.longitude);
                list.pushMap(attr);
            }
            params.putArray("poiList", list);
        }
        sendEvent("onGetReverseGeoCodeResult", params);
    }
}
