/*
 * Copyright (c) 2011-2019, Zingaya, Inc. All rights reserved.
 */

package com.dyteMobileSdk.Permissions;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.SparseArray;
import android.os.Process;

import androidx.annotation.Nullable;

import com.dyteMobileSdk.DyteInstanceManager;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.modules.core.PermissionAwareActivity;
import java.util.ArrayList;

public class RNPermissionsModule extends ReactContextBaseJavaModule implements PermissionListener {

    private final ReactApplicationContext reactContext;

    private final SparseArray<Request> mRequests = new SparseArray<Request>();
    private final String GRANTED = "granted";
    private final String DENIED = "denied";
    private final String UNAVAILABLE = "unavailable";
    private final String BLOCKED = "blocked";
    private final String PROMPTED = "prompted";
    private int mRequestCode = 0;
    private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY";


    private class Request {

        public boolean[] rationaleStatuses;
        public Callback callback;

        public Request(boolean[] rationaleStatuses, Callback callback) {
            this.rationaleStatuses = rationaleStatuses;
            this.callback = callback;
        }
    }

    public RNPermissionsModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        DyteInstanceManager.permissionsHandler = this;
    }

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


    private @Nullable
    String getFieldName(final String permission) {
        if (permission.equals("android.permission.ACCEPT_HANDOVER"))
            return "ACCEPT_HANDOVER";
        if (permission.equals("android.permission.ACCESS_BACKGROUND_LOCATION"))
            return "ACCESS_BACKGROUND_LOCATION";
        if (permission.equals("android.permission.ACCESS_COARSE_LOCATION"))
            return "ACCESS_COARSE_LOCATION";
        if (permission.equals("android.permission.ACCESS_FINE_LOCATION"))
            return "ACCESS_FINE_LOCATION";
        if (permission.equals("com.android.voicemail.permission.ADD_VOICEMAIL"))
            return "ADD_VOICEMAIL";
        if (permission.equals("android.permission.ACTIVITY_RECOGNITION"))
            return "ACTIVITY_RECOGNITION";
        if (permission.equals("android.permission.ANSWER_PHONE_CALLS"))
            return "ANSWER_PHONE_CALLS";
        if (permission.equals("android.permission.BODY_SENSORS"))
            return "BODY_SENSORS";
        if (permission.equals("android.permission.CALL_PHONE"))
            return "CALL_PHONE";
        if (permission.equals("android.permission.CAMERA"))
            return "CAMERA";
        if (permission.equals("android.permission.BLUETOOTH_CONNECT"))
            return "BLUETOOTH_CONNECT";
        if (permission.equals("android.permission.GET_ACCOUNTS"))
            return "GET_ACCOUNTS";
        if (permission.equals("android.permission.PROCESS_OUTGOING_CALLS"))
            return "PROCESS_OUTGOING_CALLS";
        if (permission.equals("android.permission.READ_CALENDAR"))
            return "READ_CALENDAR";
        if (permission.equals("android.permission.READ_CALL_LOG"))
            return "READ_CALL_LOG";
        if (permission.equals("android.permission.READ_CONTACTS"))
            return "READ_CONTACTS";
        if (permission.equals("android.permission.READ_EXTERNAL_STORAGE"))
            return "READ_EXTERNAL_STORAGE";
        if (permission.equals("android.permission.READ_PHONE_NUMBERS"))
            return "READ_PHONE_NUMBERS";
        if (permission.equals("android.permission.READ_PHONE_STATE"))
            return "READ_PHONE_STATE";
        if (permission.equals("android.permission.READ_SMS"))
            return "READ_SMS";
        if (permission.equals("android.permission.RECEIVE_MMS"))
            return "RECEIVE_MMS";
        if (permission.equals("android.permission.RECEIVE_SMS"))
            return "RECEIVE_SMS";
        if (permission.equals("android.permission.RECEIVE_WAP_PUSH"))
            return "RECEIVE_WAP_PUSH";
        if (permission.equals("android.permission.RECORD_AUDIO"))
            return "RECORD_AUDIO";
        if (permission.equals("android.permission.SEND_SMS"))
            return "SEND_SMS";
        if (permission.equals("android.permission.USE_SIP"))
            return "USE_SIP";
        if (permission.equals("android.permission.WRITE_CALENDAR"))
            return "WRITE_CALENDAR";
        if (permission.equals("android.permission.WRITE_CALL_LOG"))
            return "WRITE_CALL_LOG";
        if (permission.equals("android.permission.WRITE_CONTACTS"))
            return "WRITE_CONTACTS";
        if (permission.equals("android.permission.WRITE_EXTERNAL_STORAGE"))
            return "WRITE_EXTERNAL_STORAGE";

        return null;
    }

    private boolean permissionExists(final String permission) {
        String fieldName = getFieldName(permission);

        if (fieldName == null)
            return false;

        try {
            Manifest.permission.class.getField(fieldName);
            return true;
        } catch (NoSuchFieldException ignored) {
            return false;
        }
    }

    @ReactMethod
    public void checkMultiplePermissions(final ReadableArray permissions, final Promise promise) {
        final WritableMap output = new WritableNativeMap();
        final ArrayList<String> permissionsToCheck = new ArrayList<String>();
        int checkedPermissionsCount = 0;

        Context context = getReactApplicationContext().getBaseContext();

        for (int i = 0; i < permissions.size(); i++) {
            String permission = permissions.getString(i);

            if (!permissionExists(permission)) {
                output.putString(permission, UNAVAILABLE);
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                output.putString(
                        permission,
                        context.checkPermission(permission, Process.myPid(), Process.myUid())
                                == PackageManager.PERMISSION_GRANTED
                                ? GRANTED
                                : BLOCKED);
            } else if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                output.putString(permission, GRANTED);
            } else {
                output.putString(permission, PROMPTED);
            }
        }

        promise.resolve(output);
        return;
    }

    @ReactMethod
    public void requestMultiplePermissions(final ReadableArray permissions, final Promise promise) {
        final WritableMap output = new WritableNativeMap();
        final ArrayList<String> permissionsToCheck = new ArrayList<String>();
        int checkedPermissionsCount = 0;

        Context context = getReactApplicationContext().getBaseContext();

        for (int i = 0; i < permissions.size(); i++) {
            String permission = permissions.getString(i);

            if (!permissionExists(permission)) {
                output.putString(permission, UNAVAILABLE);
                checkedPermissionsCount++;
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                output.putString(
                        permission,
                        context.checkPermission(permission, Process.myPid(), Process.myUid())
                                == PackageManager.PERMISSION_GRANTED
                                ? GRANTED
                                : BLOCKED);

                checkedPermissionsCount++;
            } else if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                output.putString(permission, GRANTED);
                checkedPermissionsCount++;
            } else {
                permissionsToCheck.add(permission);
            }
        }

        if (permissions.size() == checkedPermissionsCount) {
            promise.resolve(output);
            return;
        }
        try {
            Activity act = getCurrentActivity();
            boolean[] rationaleStatuses = new boolean[permissions.size()];

            mRequests.put(mRequestCode, new Request(
                    rationaleStatuses,
                    new Callback() {
                        @Override
                        public void invoke(Object... args) {
                            int[] results = (int[]) args[0];

                            for (int j = 0; j < permissionsToCheck.size(); j++) {
                                String permission = permissionsToCheck.get(j);

                                if (results.length > 0 && results[j] == PackageManager.PERMISSION_GRANTED) {
                                    output.putString(permission, GRANTED);
                                } else {
                                    output.putString(permission, DENIED);
                                }
                            }
                            promise.resolve(output);
                        }
                    }));

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // if (!(act instanceof PermissionAwareActivity)) {
                //     act.requestPermissions(permissionsToCheck.toArray(new String[0]), mRequestCode);
                // } else {
                //     ac.requestPermissions(permissionsToCheck.toArray(new String[0]), mRequestCode, this);
                // }
                try {
                    if(act!=null) {
                        act.requestPermissions(permissionsToCheck.toArray(new String[0]), mRequestCode);
                    }
                } catch (Error e) {
                     PermissionAwareActivity ac = (PermissionAwareActivity) act;
                     ac.requestPermissions(permissionsToCheck.toArray(new String[0]), mRequestCode, this);
                }
            }
            mRequestCode++;
        } catch (Exception e) {
            promise.reject(ERROR_INVALID_ACTIVITY, e);
        }
    }

    public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        Request request = mRequests.get(requestCode, null);
        if(request == null) {
            return false;
        }
        request.callback.invoke(grantResults);
        mRequests.remove(requestCode);
        return mRequests.size() == 0;
    }
}
