package com.showlocationservicesdialogbox;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.*;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.location.LocationManager;
import android.text.Html;
import android.text.Spanned;
import android.view.Window;
import android.widget.Button;
import com.facebook.react.bridge.*;
import com.facebook.react.modules.core.DeviceEventManagerModule;

public class LocationServicesDialogBoxModule extends ReactContextBaseJavaModule implements ActivityEventListener {
    private Promise promiseCallback;
    private ReadableMap map;
    private Activity currentActivity;
    private static final int ENABLE_LOCATION_SERVICES = 1009;
    private static AlertDialog alertDialog;
    private Boolean isReceive = false;
    private BroadcastReceiver providerReceiver = null;
    private ReactApplicationContext RNContext;

    LocationServicesDialogBoxModule(ReactApplicationContext reactContext) {
        super(reactContext);
        RNContext = reactContext;
        reactContext.addActivityEventListener(this);
    }

    @Override
    public void onNewIntent(Intent intent) {
    }

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

    @ReactMethod
    public void checkLocationServicesIsEnabled(ReadableMap configMap, Promise promise) {
        promiseCallback = promise;
        map = configMap;
        currentActivity = getCurrentActivity();
        checkLocationService(false);
    }

    @ReactMethod
    public void forceCloseDialog() {
        try {
            if (alertDialog != null && alertDialog.isShowing() && promiseCallback != null) {
                alertDialog.cancel();
            }
        } catch (Exception ignored) {}
    }

    @ReactMethod
    public void stopListener() {
        isReceive = false;
        try {
            if (providerReceiver != null) {
                getReactApplicationContext().unregisterReceiver(providerReceiver);
                providerReceiver = null;
            }
        } catch (Exception ignored) {
        }
    }

    private void checkLocationService(Boolean activityResult) {
        if (currentActivity == null || map == null || promiseCallback == null) return;
        LocationManager locationManager = (LocationManager) currentActivity.getSystemService(Context.LOCATION_SERVICE);
        WritableMap result = Arguments.createMap();

        Boolean isEnabled = this.isEnabled(locationManager);

        if (!isEnabled) {
            if (activityResult || map.hasKey("openLocationServices") && !map.getBoolean("openLocationServices")) {
                promiseCallback.reject(new Throwable("disabled"));
                if (map.hasKey("providerListener") && map.getBoolean("providerListener")) {
                    startListener();
                }
            } else if (!map.hasKey("showDialog") || map.getBoolean("showDialog")) {
                displayPromptForEnablingGPS(currentActivity, map, promiseCallback);
            } else {
                newActivity(currentActivity);
            }
        } else {
            if (map.hasKey("providerListener") && map.getBoolean("providerListener")) {
                startListener();
            }

            result.putString("status", "enabled");
            result.putBoolean("enabled", true);
            result.putBoolean("alreadyEnabled", !activityResult);

            promiseCallback.resolve(result);
        }
    }

    private void displayPromptForEnablingGPS(final Activity activity, final ReadableMap configMap, final Promise promise) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);

        final Spanned message = (
                android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N ?
                        Html.fromHtml(configMap.getString("message"), Html.FROM_HTML_MODE_LEGACY) :
                        Html.fromHtml(configMap.getString("message"))
        );

        builder.setMessage(message)
                .setPositiveButton(configMap.getString("ok"),
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialogInterface, int id) {
                                newActivity(activity);
                                dialogInterface.dismiss();
                            }
                        })
                .setNegativeButton(configMap.getString("cancel"),
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialogInterface, int id) {
                                promise.reject(new Throwable("disabled"));
                                dialogInterface.cancel();
                            }
                        });

        alertDialog = builder.create();

        if (!configMap.hasKey("preventOutSideTouch") || configMap.getBoolean("preventOutSideTouch")) {
            alertDialog.setCanceledOnTouchOutside(false);
        }

        if (!configMap.hasKey("preventBackClick") || configMap.getBoolean("preventBackClick")) {
            alertDialog.setCancelable(false);
        }

        alertDialog.show();

        if (configMap.hasKey("style")) {
            colorAdjustment(alertDialog, configMap.getMap("style"));
        }
    }

    private void colorAdjustment(AlertDialog dialog, ReadableMap colorMap) {
        Window window = dialog.getWindow();

        if (window != null) {
            Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
            Button negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);

            if (colorMap.hasKey("backgroundColor")) {
                window.setBackgroundDrawable(new ColorDrawable(Color.parseColor(colorMap.getString("backgroundColor"))));
            }

            if (colorMap.hasKey("positiveButtonTextColor")) {
                positiveButton.setTextColor(Color.parseColor(colorMap.getString("positiveButtonTextColor")));
            }

            if (colorMap.hasKey("positiveButtonBackgroundColor")) {
                positiveButton.setBackgroundColor(Color.parseColor(colorMap.getString("positiveButtonBackgroundColor")));
            }

            if (colorMap.hasKey("negativeButtonTextColor")) {
                negativeButton.setTextColor(Color.parseColor(colorMap.getString("negativeButtonTextColor")));
            }

            if (colorMap.hasKey("negativeButtonBackgroundColor")) {
                negativeButton.setBackgroundColor(Color.parseColor(colorMap.getString("negativeButtonBackgroundColor")));
            }
        }
    }

    private void newActivity(final Activity activity) {
        final String action = android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS;
        activity.startActivityForResult(new Intent(action), ENABLE_LOCATION_SERVICES);
    }

    private void startListener() {
        try {
            providerReceiver = new LocationProviderChangedReceiver();
            getReactApplicationContext().registerReceiver(providerReceiver, new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION));
            isReceive = true;
        } catch (Exception ignored) {
        }
    }

    private Boolean isEnabled(LocationManager locationManager) {
        Boolean enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if (map != null) {
            if (map.hasKey("enableHighAccuracy") && map.getBoolean("enableHighAccuracy")) {
                // High accuracy needed. Require NETWORK_PROVIDER.
                enabled = enabled && locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            } else {
                // Either highAccuracy is not a must which means any location will suffice
                // or it is not specified which means again that any location will do.
                enabled = enabled || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            }
        }

        return enabled;
    }

    private void sendEvent() {
        if (isReceive) {
            LocationManager locationManager = (LocationManager) currentActivity.getSystemService(Context.LOCATION_SERVICE);
            WritableMap params = Arguments.createMap();
            if (locationManager != null) {
                boolean enabled = this.isEnabled(locationManager);

                params.putString("status", (enabled ? "enabled" : "disabled"));
                params.putBoolean("enabled", enabled);

                if (RNContext != null) {
                    RNContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("locationProviderStatusChange", params);
                }
            }
        }
    }

    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        if (requestCode == ENABLE_LOCATION_SERVICES) {
            currentActivity = activity;
            checkLocationService(true);
        }
    }

    private final class LocationProviderChangedReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (action != null && action.matches("android.location.PROVIDERS_CHANGED")) {
                sendEvent();
            }
        }
    }
}