package com.mappls.sdk.intouch.reactnative;


import android.location.Location;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.mappls.sdk.intouch.InTouch;
import com.mappls.sdk.intouch.callbacks.IAuthListener;
import com.mappls.sdk.services.account.MapplsAccountManager;
import com.mappls.sdk.tracking.Config;
import com.mappls.sdk.tracking.CustomConfig;
import com.mappls.sdk.tracking.DeviceStateChangeObserver;
import com.mappls.sdk.tracking.TrackingStateObserver;
import com.mappls.sdk.tracking.callbacks.ILocationUpdate;
import com.mappls.sdk.tracking.core.model.autotrack.AutoTrackingConfig;
import com.mappls.sdk.tracking.core.model.autotrack.WorkTiming;
import com.mappls.sdk.tracking.utils.TrackingError;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;

public class MapplsIntouchModule extends ReactContextBaseJavaModule implements TrackingStateObserver.OnTrackingStateChangeListener, DeviceStateChangeObserver.OnDeviceStateChangeListener {

    public static final String TAG = MapplsIntouchModule.class.getSimpleName();
    private final ReactApplicationContext reactContext;

    public MapplsIntouchModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

    @Override
    public String getName() {
        return "MapplsIntouch";
    }

    @Nullable
    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put("BEACON_PRIORITY_FAST", InTouch.BEACON_PRIORITY_FAST);
        constants.put("BEACON_PRIORITY_OPTIMAL", InTouch.BEACON_PRIORITY_OPTIMAL);
        constants.put("BEACON_PRIORITY_SLOW", InTouch.BEACON_PRIORITY_SLOW);
        constants.put("BEACON_PRIORITY_DRIVING", InTouch.BEACON_PRIORITY_DRIVING);
        return constants;
    }

    @ReactMethod
    public void createDevice(String deviceName, String clientId, String clientSecret, String deviceCode, Promise promise) {
        String mDeviceCode = null;
        if (!deviceCode.equals("")) {
            mDeviceCode = deviceCode;
        }
        MapplsAccountManager.getInstance().setAtlasClientId(clientId);
        MapplsAccountManager.getInstance().setAtlasClientSecret(clientSecret);

        //Log.e(TAG, "initialize: " + mDeviceCode);
        InTouch.initialize(deviceName, mDeviceCode, 0, new IAuthListener() {
            @Override
            public void onSuccess(Long entityId) {
                //Log.e("sss", "success");
               // callback.invoke("success", entityId);
                promise.resolve(entityId.toString());
            }

            @Override
            public void onError(String reason, String identifier, String description) {
                //callback.invoke(null, reason);
                promise.reject(identifier,reason);
               // Log.e("sss", reason);
            }
        });
    }

    @ReactMethod
    public void startTracking(Integer priority, boolean enableRequestPermissionIfMissing, int activityDetectionTimeInterval) {
        //new CustomConfig.Builder(reactContext).build();
        configBuilder(priority, enableRequestPermissionIfMissing, activityDetectionTimeInterval);
        InTouch.startTracking();
    }

    @ReactMethod
    public void startTrackingWithCustomConfig(final ReadableMap map) {
        customConfig(map);
//        new CustomConfig.Builder(reactContext)
//                .setTimeWhileMovingInSec(1)
//                .setBeaconStandByTimeInMins(10)
//                .setEnableRequestPermissionIfMissing(true)
//                .setDetectDrive(true)
//                .setBeaconStartTimeInSec(1616477366)
//                .setBeaconEndTimeInSec(1616477726)
//                .build();
        InTouch.startTracking();
    }

    @ReactMethod
    public void stopTracking() {
        InTouch.stopTracking();
    }

    @ReactMethod
    public void isRunning(Promise promise) {
        boolean isRunning = InTouch.isRunning();
        try {
            promise.resolve(isRunning);
        } catch (Exception e) {
            promise.reject(" isRunning Error", e);
        }

    }

    private void configBuilder(int priority, boolean enableRequestPermissionIfMissing, int activityDetectionTimeInterval) {
        new Config.Builder(reactContext).setPriority(priority).setEnableRequestPermissionIfMissing(enableRequestPermissionIfMissing).setActivityDetectionTimeInterval(activityDetectionTimeInterval).build();
    }

    private void sendEvent(ReactContext reactContext,
                           String eventName,
                           @Nullable Object params) {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
    }

    @ReactMethod
    public void addTrackingStateListener() {
        InTouch.addTrackingStateListener(this);

    }

    @ReactMethod
    public void addDeviceStateChangeListener() {
        InTouch.addDeviceStateChangeListener(this);
    }

    //@ReactMethod
    public void customConfig(ReadableMap map){
        CustomConfig.Builder customConfig = new CustomConfig.Builder(reactContext);
        if (map.hasKey("enableAutoStartTracking")&& !map.isNull("enableAutoStartTracking")){
            boolean enableAutoStartTracking = map.getBoolean("enableAutoStartTracking");
            customConfig.setEnableAutoStartTracking(enableAutoStartTracking);
        }
//        if (map.hasKey("beaconStartTimeInSec")&& !map.isNull("beaconStartTimeInSec")){
//            double beaconStartTimeInSec = map.getDouble("beaconStartTimeInSec");
//            customConfig.setBeaconStartTimeInSec((long)beaconStartTimeInSec);
//        }
//        if (map.hasKey("beaconEndTimeInSec")&& !map.isNull("beaconEndTimeInSec")){
//            double beaconEndTimeInSec = map.getDouble("beaconEndTimeInSec");
//            customConfig.setBeaconEndTimeInSec((long)beaconEndTimeInSec);
//        }
        if (map.hasKey("timeWhileMovingInSec")&& !map.isNull("timeWhileMovingInSec")){
            int timeWhileMovingInSec = map.getInt("timeWhileMovingInSec");
            customConfig.setTimeWhileMovingInSec(timeWhileMovingInSec);
        }
        if (map.hasKey("standByTimeInMins")&& !map.isNull("standByTimeInMins")){
            int standByTimeInMins = map.getInt("standByTimeInMins");
            customConfig.setBeaconStandByTimeInMins(standByTimeInMins);
        }
        if (map.hasKey("timeDelayFromMovingToStopInMillis")&& !map.isNull("timeDelayFromMovingToStopInMillis")){
            int timeDelayFromMovingToStopInMillis = map.getInt("timeDelayFromMovingToStopInMillis");
            customConfig.setTimeDelayFromMovingToStopInMillis(timeDelayFromMovingToStopInMillis);
        }
        if (map.hasKey("activityDetectionTimeInterval")&& !map.isNull("activityDetectionTimeInterval")){
            int activityDetectionTimeInterval = map.getInt("activityDetectionTimeInterval");
            customConfig.setActivityDetectionTimeInterval(activityDetectionTimeInterval);
        }
        if (map.hasKey("displacement")&& !map.isNull("displacement")){
            int displacement = map.getInt("displacement");
            customConfig.setDisplacement(displacement);
        }
        if (map.hasKey("defaultAccuracy")&& !map.isNull("defaultAccuracy")){
            int defaultAccuracy = map.getInt("defaultAccuracy");
            customConfig.setDefaultAccuracy(defaultAccuracy);
        }
        if (map.hasKey("enableRequestPermissionIfMissing")&& !map.isNull("enableRequestPermissionIfMissing")){
            boolean enableRequestPermissionIfMissing = map.getBoolean("enableRequestPermissionIfMissing");
            customConfig.setEnableRequestPermissionIfMissing(enableRequestPermissionIfMissing);
        }
        if (map.hasKey("isAccelerometerSensorRequired")&& !map.isNull("isAccelerometerSensorRequired")){
            boolean isAccelerometerSensorRequired = map.getBoolean("isAccelerometerSensorRequired");
            customConfig.setIsAccelerometerSensorRequired(isAccelerometerSensorRequired);
        }
        if (map.hasKey("isGyroMeterSensorRequired")&& !map.isNull("isGyroMeterSensorRequired")){
            boolean isGyroMeterSensorRequired = map.getBoolean("isGyroMeterSensorRequired");
            customConfig.setIsGyroMeterSensorRequired(isGyroMeterSensorRequired);
        }
        if (map.hasKey("isProximitySensorRequired")&& !map.isNull("isProximitySensorRequired")){
            boolean isProximitySensorRequired = map.getBoolean("isProximitySensorRequired");
            customConfig.setIsProximitySensorRequired(isProximitySensorRequired);
        }
        if (map.hasKey("isBarometerSensorRequired")&& !map.isNull("isBarometerSensorRequired")){
            boolean isBarometerSensorRequired = map.getBoolean("isBarometerSensorRequired");
            customConfig.setIsBarometerSensorRequired(isBarometerSensorRequired);
        }
        if (map.hasKey("isTemperatureSensorRequired")&& !map.isNull("isTemperatureSensorRequired")){
            boolean isTemperatureSensorRequired = map.getBoolean("isTemperatureSensorRequired");
            customConfig.setIsTemperatureSensorRequired(isTemperatureSensorRequired);
        }
        if (map.hasKey("isLightSensorRequired")&& !map.isNull("isLightSensorRequired")){
            boolean isLightSensorRequired = map.getBoolean("isLightSensorRequired");
            customConfig.setIsLightSensorRequired(isLightSensorRequired);
        }
        if (map.hasKey("isPhoneStateListenerRequired")&& !map.isNull("isPhoneStateListenerRequired")){
            boolean isPhoneStateListenerRequired = map.getBoolean("isPhoneStateListenerRequired");
            customConfig.setIsPhoneStateListenerRequired(isPhoneStateListenerRequired);
        }
        if (map.hasKey("isDrivingMode")&& !map.isNull("isDrivingMode")){
            boolean isDrivingMode = map.getBoolean("isDrivingMode");
            customConfig.isDrivingMode(isDrivingMode);
        }
        if(map.hasKey("autoTrackingConfig") && !map.isNull("autoTrackingConfig")) {
            ReadableMap autoTrackingConfig = map.getMap("autoTrackingConfig");
            if(autoTrackingConfig.hasKey("endTimeConfig") && !autoTrackingConfig.isNull("endTimeConfig")) {
                customConfig.setAutoTrackingConfig(new AutoTrackingConfig(getWorkTiming(autoTrackingConfig.getMap("endTimeConfig"))));
            }
        }
        customConfig.setDetectDrive(true);
        customConfig.build();
    }

    private WorkTiming getWorkTiming(ReadableMap map) {
        if(map.hasKey("hour") && !map.isNull("hour")) {
            int hour = map.getInt("hour");
            String ampm = "am";
            int min = 0;
            int sec = 0;
            if(map.hasKey("amPm") && !map.isNull("amPm")) {
                ampm = map.getString("amPm");
            }
            if(map.hasKey("minute") && !map.isNull("minute")) {
                min = map.getInt("minute");
            }
            if(map.hasKey("second") && !map.isNull("second")) {
                sec = map.getInt("second");
            }
            return new WorkTiming(hour, min, sec, ampm);
        } else {
            return null;
        }
    }

    @ReactMethod
    public void removeTrackingStateListener() {
        InTouch.removeTrackingStateListener(this);
    }

    @ReactMethod
    public void removeDeviceStateChangeListener() {
        InTouch.removeDeviceStateChangeListener(this);
    }

    @ReactMethod
    public void isInitialized(Promise promise) {
        boolean isInitialized = InTouch.isInitialized();
        try {
            promise.resolve(isInitialized);
        } catch (Exception e) {
            promise.reject("Initialization Error", e);
        }
    }

    @ReactMethod
    public void getCurrentLocationUpdate(boolean enableRequestPermissionIfMissing, final Promise promise) {
        new Config.Builder(reactContext).setEnableRequestPermissionIfMissing(enableRequestPermissionIfMissing).build();
        InTouch.getCurrentLocation(new ILocationUpdate() {

            @Override
            public void onLocationUpdate(Location location, boolean isFake, boolean isAccurate) {
                WritableMap locationUpdate = Arguments.createMap();
                locationUpdate.putBoolean("isFake", isFake);
                locationUpdate.putBoolean("isAccurate", isAccurate);
                WritableMap locationMap = Arguments.createMap();

                locationMap.putDouble("latitude", location.getLatitude());
                locationMap.putDouble("longitude", location.getLongitude());
                locationMap.putDouble("speed", location.getSpeed());
                locationMap.putDouble("accuracy", location.getAccuracy());
                locationMap.putDouble("bearing", location.getBearing());
                locationMap.putDouble("altitude", location.getAltitude());

                locationUpdate.putMap("location", locationMap);
                promise.resolve(locationUpdate);
            }

            @Override
            public void onError(String s) {
                promise.reject("getCurrentLocation Error: ", s);
            }
        });
    }

    @Override
    public void onError(TrackingError trackingError) {
        //callback.invoke("code: "+trackingError.getCode()+" message: "+trackingError.getMessage(),"");
        sendEvent(reactContext, "TrackingErrorListener", "Error code: " + trackingError.getCode() + " Error message: " + trackingError.getMessage());
    }

    @Override
    public void onTrackingStart() {
        //callback.invoke(null,"onTrackingStart");

        sendEvent(reactContext, "TrackingStateListener", "onTrackingStart");
    }

    @Override
    public void onTrackingStop() {
        //callback.invoke(null,"onTrackingStop");
        sendEvent(reactContext, "TrackingStateListener", "onTrackingStop");
    }

    @Override
    public void onBatteryChange(int batteryInfo) {
        sendEvent(reactContext, "BatteryChangeListener",  batteryInfo);
    }

    @Override
    public void onDeviceStateChange(int deviceState) {
        sendEvent(reactContext, "DeviceStateChangeListener", deviceState);
    }

    @Override
    public void isGprsAvailable(boolean isGprs) {
        sendEvent(reactContext, "GprsAvailableListener", isGprs);
    }

    @Override
    public void isGpsAvailable(boolean isGps) {
        sendEvent(reactContext, "GpsAvailableListener", isGps);
    }
}
