/*
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 * All rights reserved.
 * Confidential and Proprietary - Qualcomm Technologies, Inc.
 */

package com.qualcomm.snapdragon.spaces.helpers;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.util.Log;
import android.view.Display;

import java.util.ArrayList;
import com.qualcomm.qti.device.access.*;

public class PermissionHelper {
    private static final String TAG = "Spaces-PermissionHelper";

    private static final String _preferencesAppPermissionsDeniedKey = "APP_PERMISSIONS_DENIED";
    private static final String _preferencesSpacesPermissionsDeniedKey = "SERVICES_PERMISSIONS_DENIED";
    private static final int _permissionsRequestCode = 42;

    private Activity _callingActivity;
    private PermissionHandler _handler;

    public PermissionHelper(Activity activity, PermissionHandler handler) {
        _callingActivity = activity;
        _handler = handler;
    }

    public boolean CheckSpacesServicePermissions() {
        if (shouldAskSpacesApplicationPermissions()) {
            if(getRuntimeLauncherIntent() != null) {
                askSpacesApplicationPermissions();
            }
            else{
                warnSpacesApplicationPermissions();
            }
            return true;
        }
        return false;
    }

    private void ShowAlertDialog(String title, String message, String positiveButtonText, DialogInterface.OnClickListener positiveEvent, String negativeButtonText, DialogInterface.OnClickListener negativeEvent) {
        boolean darkMode = (_callingActivity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES ? true : false;
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(_callingActivity, darkMode ? AlertDialog.THEME_DEVICE_DEFAULT_DARK : AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
        dialogBuilder.setTitle(title);
        dialogBuilder.setMessage(message);
        dialogBuilder.setPositiveButton(positiveButtonText, positiveEvent);
        dialogBuilder.setNegativeButton(negativeButtonText, negativeEvent);
        dialogBuilder.show();
    }

    private void savePreference(String key, boolean val) {
        _callingActivity.getPreferences(Context.MODE_PRIVATE).edit().putBoolean(key, val).apply();
    }

    private boolean hasPreference(String key) {
        return _callingActivity.getPreferences(Context.MODE_PRIVATE).contains(key);
    }

    public Intent getRuntimeLauncherIntent(){
        String packageName;
        try {
            DeviceAccessManager dam = DeviceAccessManager.getInstance(null);
            packageName = dam.getRuntimePackageName(_callingActivity);
        } catch(Exception e) {
            packageName = "com.qualcomm.qti.spaces.services";
        }

        return _callingActivity.getPackageManager().getLaunchIntentForPackage(packageName);
    }

    private void askSpacesApplicationPermissions() {
        // Services has a launcher intent
        ShowAlertDialog("Camera access for the OpenXR runtime",
            "The OpenXR runtime requires permissions to be enabled for both the 'Camera' and 'Display over other Apps' settings. Select 'Configure' to set permissions, otherwise some perception features may not function properly.",
            "Configure",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                   Log.v(TAG, "Starting the OpenXR runtime to ask for the required permissions.");
                   _handler.handleSpacesServicePermissionConfigure();
                }
            },
            "Continue",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    Log.v(TAG, "The permissions access has been denied. Saving this into the preferences to not ask again.");
                    savePreference(_preferencesSpacesPermissionsDeniedKey, true);
                    _handler.handleSpacesServicePermissionContinue();
                }
        });
    }

    private void warnSpacesApplicationPermissions(){
        // No launcher intent, we can't open settings
        ShowAlertDialog("Camera access for the OpenXR runtime",
            "The OpenXR runtime requires permissions to be enabled for both the 'Camera' and 'Display over other Apps' settings. Set them through Android settings, otherwise some perception features may not function properly.",
            "Exit",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    Log.v(TAG, "Closing the Spaces app.");
                    _handler.handleSpacesServicePermissionExit();
                }
            },
            "Continue",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    Log.v(TAG, "The permissions access has been denied. Saving this into the preferences to not ask again.");
                    savePreference(_preferencesSpacesPermissionsDeniedKey, true);
                    _handler.handleSpacesServicePermissionContinue();
                }
            });
    }

    private boolean shouldAskSpacesApplicationPermissions() {
        if (_callingActivity.getPreferences(Context.MODE_PRIVATE).getBoolean(_preferencesSpacesPermissionsDeniedKey, false)) {
            Log.e(TAG, "Permissions for the OpenXR runtime were denied before. Please enable permissions in the settings.");
            return false;
        }

        String packageName;
        try {
            DeviceAccessManager dam = DeviceAccessManager.getInstance(null);
            packageName = dam.getRuntimePackageName(_callingActivity);
        } catch(Exception e) {
            packageName = "com.qualcomm.qti.spaces.services";
        }

        for (final PackageInfo packageInfo : _callingActivity.getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS)) {
            if (!packageInfo.packageName.equals(packageName) || packageInfo.requestedPermissions == null) {
                continue;
            }

            Log.v(TAG, "Checking permissions for the OpenXR runtime.");
            for (int i = 0, len = packageInfo.requestedPermissions.length; i < len; i++) {
                if (packageInfo.requestedPermissions[i].equals(Manifest.permission.SYSTEM_ALERT_WINDOW)) {
                    final AppOpsManager manager = (AppOpsManager) _callingActivity.getSystemService(Context.APP_OPS_SERVICE);
                    int mode;
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
                        mode = manager.unsafeCheckOpNoThrow(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, packageInfo.applicationInfo.uid, packageInfo.packageName);
                    } else {
                        mode = manager.checkOpNoThrow(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, packageInfo.applicationInfo.uid, packageInfo.packageName);
                    }

                    if (mode != AppOpsManager.MODE_ALLOWED) {
                        return true;
                    }
                }

                if (packageInfo.requestedPermissions[i].equals(Manifest.permission.CAMERA)) {
                    if ((packageInfo.requestedPermissionsFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != PackageInfo.REQUESTED_PERMISSION_GRANTED) {
                        return true;
                    }
                }

            }
        }
        Log.v(TAG, "Permissions have been given to the OpenXR runtime!");
        return false;
    }

    public void CheckApplicationPermissions(boolean servicesCameraPermissionAsked) {
        if (_callingActivity.getPreferences(Context.MODE_PRIVATE).getBoolean(_preferencesAppPermissionsDeniedKey, false)) {
            Log.e(TAG, "Application permissions for the application were denied before. Please enable it in the settings.");
            _handler.handlePermissionsDenied();
            return;
        }

        ArrayList<String> permissionsToRequestList = new ArrayList<String>();
        try {
            final PackageInfo myPackageInfo = _callingActivity.getPackageManager().getPackageInfo(_callingActivity.getPackageName(),PackageManager.GET_PERMISSIONS);
            for (int i = 0, len = myPackageInfo.requestedPermissions.length; i < len; i++) {
                String permission = myPackageInfo.requestedPermissions[i];
                if (!permission.contains("android.permission")) {
                    continue;
                }
                if ((myPackageInfo.requestedPermissionsFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
                    if (hasPreference(permission)) {
                        Log.v(TAG, "Permission: " + permission + " was denied before. Please enable in the settings!");
                        continue;
                    }
		            /* Specific checks to filter out certain permissions on different Android platforms to avoid an Alert Dialog when one is not actually needed */
                    if (android.os.Build.VERSION.SDK_INT <= 32) //android.os.Build.VERSION_CODES.S_V2)
                    {
                        if (permission.startsWith("android.permission.READ_MEDIA") || permission.equals("android.permission.MANAGE_EXTERNAL_STORAGE")) {
                            Log.v(TAG, "Skipping " + permission + " permission on Android S or earlier!");
                            continue;
                        }
                    } else { /* build version is > S so Android T or newer */
                        /* Permission string hardcoded because it's not available when Target API is lower than 33 */
                        if (permission.equals("android.permission.POST_NOTIFICATIONS") ||
                                permission.equals("android.permission.READ_EXTERNAL_STORAGE")) {
                            Log.v(TAG, "Skipping " + permission + " permission on Android T!");
                            continue;
                        }
                    }

                    permissionsToRequestList.add(permission);
                    /* Show our popup about preferences if we initially have grantable permission that have yet to be granted. */
                    Log.v(TAG, "Permission: " + permission + " (granted: " + (myPackageInfo.requestedPermissionsFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) + ")");
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
        }

        if (permissionsToRequestList.size() == 0) {
            Log.v(TAG, "All permissions have been granted. Skipping permissions dialog.");
            _handler.handlePermissionsAccepted();
        } else {
            Log.v(TAG, "Permissions" + permissionsToRequestList.toString() + " have not been granted. Displaying info view to grant permissions.");
            String message = "This application requires permissions granted to function properly. Do you want to request permissions?";
            if (servicesCameraPermissionAsked) {
                message = "In addition to the OpenXR runtime, this application requires permissions granted for features to function properly. Do you want to request permissions?";
            }
            ShowAlertDialog("Permissions Request",
                    message,
                    "Yes",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            Log.v(TAG, "Displaying system permissions dialog(s), unless a permission was denied twice.");
                            _callingActivity.requestPermissions(permissionsToRequestList.toArray(new String[permissionsToRequestList.size()]), _permissionsRequestCode);
                        }
                    },
                    "No",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            Log.v(TAG, "User wishes to not grant permissions");
                            savePreference(_preferencesAppPermissionsDeniedKey, true);
                            _handler.handlePermissionsDenied();
                        }
                    });
        }
    }

    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
        boolean anyPermissionsDenied = false;
        if (requestCode == _permissionsRequestCode && results.length > 0) {
            for (int i = 0; i < permissions.length; i++) {
                Log.v(TAG, "Permission " + permissions[i] + " will be checked. Should show rationale: " + _callingActivity.shouldShowRequestPermissionRationale(permissions[i]));
                if (results[i] != PackageManager.PERMISSION_GRANTED) {
                    savePreference(permissions[i], true);
                    anyPermissionsDenied = true;
                }
            }
        }
        _handler.handlePermissionsChecksCompleted(anyPermissionsDenied);
    }

    public interface PermissionHandler {
        public void handleSpacesServicePermissionContinue();
        public void handleSpacesServicePermissionConfigure();
        public void handleSpacesServicePermissionExit();
        public void handlePermissionsDenied();
        public void handlePermissionsAccepted();
        public void handlePermissionsChecksCompleted(boolean anyPermissionsDenied);
    }

}
