package cordova.plugin.documentreader;

import android.Manifest;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.app.ActivityCompat;

import android.graphics.BitmapFactory;
import android.widget.Toast;
import android.util.Base64;

import com.regula.documentreader.api.completions.IDocumentReaderCompletion;
import com.regula.documentreader.api.completions.IDocumentReaderInitCompletion;
import com.regula.documentreader.api.completions.IDocumentReaderPrepareCompletion;
import com.regula.documentreader.api.enums.DocReaderAction;
import com.regula.documentreader.api.params.ImageInputParam;

import com.regula.documentreader.api.results.DocumentReaderScenario;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;

import android.util.Log;

import java.util.ArrayList;
import java.util.Objects;

public class DocumentReader extends CordovaPlugin {
    private CallbackContext callbackContext;
    private Context context;
    int timer = -2000000000;

    private Context getContext() {
        return context;
    }

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        this.callbackContext = callbackContext;
        context = this.cordova.getActivity();
        switch (action) {
            case "getAPIVersion":
                this.getAPIVersion();
                return true;
            case "getAvailableScenarios":
                this.getAvailableScenarios();
                return true;
            case "isRFIDAvailableForUse":
                this.isRFIDAvailableForUse();
                return true;
            case "getCoreMode":
                this.getCoreMode();
                return true;
            case "getCoreVersion":
                this.getCoreVersion();
                return true;
            case "getDatabaseDate":
                this.getDatabaseDate();
                return true;
            case "getDatabaseID":
                this.getDatabaseID();
                return true;
            case "getDatabaseVersion":
                this.getDatabaseVersion();
                return true;
            case "getDocumentReaderIsReady":
                this.getDocumentReaderIsReady();
                return true;
            case "getDocumentReaderStatus":
                this.getDocumentReaderStatus();
                return true;
            case "getDatabaseCountriesNumber":
                this.getDatabaseCountriesNumber();
                return true;
            case "getDatabaseDocumentsNumber":
                this.getDatabaseDocumentsNumber();
                return true;
            case "selectedScenario":
                this.selectedScenario();
                return true;
            case "getSessionLogFolder":
                this.getSessionLogFolder();
                return true;
            case "getDatabaseDescription":
                this.getDatabaseDescription();
                return true;
            case "initializeReader":
                Object license = args.get(0);
                this.initializeReader(license);
                return true;
            case "prepareDatabase":
                this.prepareDatabase(args.getString(0));
                return true;
            case "recognizeImage":
                this.recognizeImage(args.getString(0));
                return true;
            case "recognizeImageFrame":
                this.recognizeImageFrame(args.getString(0), args.getJSONObject(1));
                return true;
            case "recognizeImageWithOpts":
                this.recognizeImageWithOpts(args.getJSONObject(0), args.getString(1));
                return true;
            case "recognizeVideoFrame":
                this.recognizeVideoFrame(args.getString(0), args.getJSONObject(1));
                return true;
            case "runAutoUpdate":
                this.runAutoUpdate(args.getString(0));
                return true;
            case "setConfig":
                this.setConfig(args.getJSONObject(0));
                return true;
            case "setCameraSessionIsPaused":
                this.setCameraSessionIsPaused(args.getBoolean(0));
                return true;
            case "setRfidScenario":
                this.setRfidScenario(args.getJSONObject(0));
                return true;
            case "showDialog":
                this.showDialog(args.getString(0));
                return true;
            case "showScanner":
                this.showScanner();
                return true;
            case "showScannerWithCameraID":
                this.showScannerWithCameraID(args.getInt(0));
                return true;
            case "showScannerWithCameraIDAndOpts":
                this.showScannerWithCameraIDAndOpts(args.getInt(0), args.getJSONObject(1));
                return true;
            case "startNewPage":
                this.startNewPage();
                return true;
            case "startNewSession":
                this.startNewSession();
                return true;
            case "startRFIDReader":
                this.startRFIDReader();
                return true;
            case "stopRFIDReader":
                this.stopRFIDReader();
                return true;
            case "stopScanner":
                this.stopScanner();
                return true;
            case "deinitializeReader":
                this.deinitializeReader();
                break;
            case "isAuthenticatorAvailableForUse":
                this.isAuthenticatorAvailableForUse();
                break;
            case "getConfig":
                this.getConfig();
                break;
            case "getRfidScenario":
                this.getRfidScenario();
                break;
            case "getLicenseExpiryDate":
                this.getLicenseExpiryDate();
                break;
            case "getLicenseCountryFilter":
                this.getLicenseCountryFilter();
                break;
            case "licenseIsRfidAvailable":
                this.licenseIsRfidAvailable();
                break;
            case "recognizeImageWithImageInputParams":
                this.recognizeImageWithImageInputParams(args.getString(0), args.getJSONObject(1));
                break;
            case "permissionRead":
                this.permissionRead();
                break;
        }

        return false;
    }

    public void deinitializeReader() {
        com.regula.documentreader.api.DocumentReader.Instance().deinitializeReader();
        callbackContext.success();
    }

    public void isAuthenticatorAvailableForUse() {
        callbackContext.success(com.regula.documentreader.api.DocumentReader.Instance().isAuthenticatorAvailableForUse() ? 1 : 0);
    }

    public void getConfig() throws JSONException {
        callbackContext.success(RegulaConfig.getConfig(com.regula.documentreader.api.DocumentReader.Instance()).toString());
    }

    public void getRfidScenario() {
        callbackContext.success(com.regula.documentreader.api.DocumentReader.Instance().rfidScenario().toJson());
    }

    public void selectedScenario() throws JSONException {
        ArrayList<String> scenarioNames = new ArrayList<>();
        for (int i = 0; i < com.regula.documentreader.api.DocumentReader.Instance().availableScenarios.size(); i++)
            scenarioNames.add(com.regula.documentreader.api.DocumentReader.Instance().availableScenarios.get(i).name);
        callbackContext.success(JSONConstructor.generateDocumentReaderScenario(com.regula.documentreader.api.DocumentReader.Instance().availableScenarios.get(scenarioNames.indexOf(com.regula.documentreader.api.DocumentReader.Instance().processParams().scenario))).toString());
    }

    public void getLicenseExpiryDate() {
        if (com.regula.documentreader.api.DocumentReader.Instance().license().getExpiryDate() == null)
            callbackContext.success("null");
        else
            callbackContext.success(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().license().getExpiryDate()).toString());
    }

    public void getLicenseCountryFilter() {
        JSONArray list = new JSONArray();
        if (com.regula.documentreader.api.DocumentReader.Instance().license().getCountryFilter() == null)
            callbackContext.success("null");
        else {
            for (String item : Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().license().getCountryFilter()))
                list.put(item);
            callbackContext.success(list.toString());
        }
    }

    public void licenseIsRfidAvailable() {
        callbackContext.success(com.regula.documentreader.api.DocumentReader.Instance().license().isRfidAvailable() ? 1 : 0);
    }

    public void getDocumentReaderIsReady() {
        callbackContext.success(com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady() ? 1 : 0);
    }

    public void getDocumentReaderStatus() {
        callbackContext.success(com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderStatus());
    }

    public void isRFIDAvailableForUse() {
        callbackContext.success(com.regula.documentreader.api.DocumentReader.Instance().isRFIDAvailableForUse() ? 1 : 0);
    }

    private void initializeReader(Object license) {
        if (!com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady())
            com.regula.documentreader.api.DocumentReader.Instance().initializeReader(cordova.getActivity(), Base64.decode(license.toString(), Base64.DEFAULT), getInitCompletion());
        else
            callbackContext.error("already initialized");
    }

    public void startNewSession() {
        com.regula.documentreader.api.DocumentReader.Instance().startNewSession();
        callbackContext.success();
    }

    public void startNewPage() {
        com.regula.documentreader.api.DocumentReader.Instance().startNewPage();
        callbackContext.success();
    }

    public void recognizeImageWithImageInputParams(String base64Image, final JSONObject params) throws JSONException {
        if (com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady())
            com.regula.documentreader.api.DocumentReader.Instance().recognizeImage(bitmapFromBase64String(base64Image), new ImageInputParam(params.getInt("width"), params.getInt("height"), params.getInt("type")), getCompletion());
        else
            callbackContext.error("document reader not ready");
    }

    public void recognizeImageWithOpts(final JSONObject opts, String base64Image) throws JSONException {
        RegulaConfig.setConfig(com.regula.documentreader.api.DocumentReader.Instance(), opts, getContext());
        recognizeImage(base64Image);
    }

    public void recognizeImage(String base64Image) {
        if (com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady())
            com.regula.documentreader.api.DocumentReader.Instance().recognizeImage(bitmapFromBase64String(base64Image), getCompletion());
        else
            callbackContext.error("document reader not ready");
    }

    public void recognizeImageFrame(String base64Image, final JSONObject opts) throws JSONException {
        if (com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady())
            com.regula.documentreader.api.DocumentReader.Instance().recognizeImageFrame(bitmapFromBase64String(base64Image), new ImageInputParam(opts.getInt("width"), opts.getInt("height"), opts.getInt("type")), getCompletion());
        else
            callbackContext.error("document reader not ready");
    }

    public void recognizeVideoFrame(String byteString, final JSONObject opts) throws JSONException {
        if (com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady())
            com.regula.documentreader.api.DocumentReader.Instance().recognizeVideoFrame(byteString.getBytes(), new ImageInputParam(opts.getInt("width"), opts.getInt("height"), opts.getInt("type")), getCompletion());
        else
            callbackContext.error("document reader not ready");
    }

    public void showScannerWithCameraID(int cameraID) {
        if (com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady())
            com.regula.documentreader.api.DocumentReader.Instance().showScanner(getContext(), cameraID, getCompletion());
        else
            callbackContext.error("document reader not ready");
    }

    public void showScanner() {
        showScannerWithCameraID(-1);
    }

    public void showScannerWithCameraIDAndOpts(int cameraID, final JSONObject opts) throws JSONException {
        if (com.regula.documentreader.api.DocumentReader.Instance().getDocumentReaderIsReady()) {
            RegulaConfig.setConfig(com.regula.documentreader.api.DocumentReader.Instance(), opts, getContext());
            com.regula.documentreader.api.DocumentReader.Instance().showScanner(getContext(), cameraID, getCompletion());
        } else
            callbackContext.error("document reader not ready");
    }

    public void stopScanner() {
        com.regula.documentreader.api.DocumentReader.Instance().stopScanner(getContext());
        callbackContext.success();
    }

    public void startRFIDReader() {
        com.regula.documentreader.api.DocumentReader.Instance().startRFIDReader(getContext(), getCompletion());
    }

    public void stopRFIDReader() {
        com.regula.documentreader.api.DocumentReader.Instance().stopRFIDReader(getContext());
        callbackContext.success();
    }

    public void prepareDatabase(String dbID) {
        com.regula.documentreader.api.DocumentReader.Instance().prepareDatabase(cordova.getActivity(), dbID, getPrepareCompletion());
    }

    public void runAutoUpdate(String dbID) {
        com.regula.documentreader.api.DocumentReader.Instance().runAutoUpdate(cordova.getActivity(), dbID, getPrepareCompletion());
    }

    public void setRfidScenario(final JSONObject opts) throws JSONException {
        RegulaConfig.setRfidScenario(opts);
        callbackContext.success();
    }

    public void getAPIVersion() {
        callbackContext.success(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).api);
    }

    public void getCoreVersion() {
        callbackContext.success(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).core);
    }

    public void getCoreMode() {
        callbackContext.success(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).coreMode);
    }

    public void getDatabaseID() {
        callbackContext.success(Objects.requireNonNull(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).database).databaseID);
    }

    public void getDatabaseVersion() {
        callbackContext.success(Objects.requireNonNull(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).database).version);
    }

    public void getDatabaseDate() {
        callbackContext.success(Objects.requireNonNull(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).database).date);
    }

    public void getDatabaseDescription() {
        callbackContext.success(Objects.requireNonNull(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).database).databaseDescription);
    }

    public void getDatabaseCountriesNumber() {
        callbackContext.success(Objects.requireNonNull(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).database).countriesNumber);
    }

    public void getDatabaseDocumentsNumber() {
        callbackContext.success(Objects.requireNonNull(Objects.requireNonNull(com.regula.documentreader.api.DocumentReader.Instance().version).database).documentsNumber);
    }

    public void getAvailableScenarios() throws JSONException {
        JSONArray scenarios = new JSONArray();
        for (DocumentReaderScenario scenario : com.regula.documentreader.api.DocumentReader.Instance().availableScenarios)
            scenarios.put(JSONConstructor.generateDocumentReaderScenario(scenario));
        callbackContext.success(scenarios.toString());
    }

    public void getSessionLogFolder() {
        callbackContext.success(com.regula.documentreader.api.DocumentReader.Instance().processParams().sessionLogFolder);
    }

    private void showDialog(String msg) {
        Toast.makeText(cordova.getActivity(), msg, Toast.LENGTH_LONG).show();
    }

    public void setConfig(final JSONObject opts) throws JSONException {
        RegulaConfig.setConfig(com.regula.documentreader.api.DocumentReader.Instance(), opts, getContext());
        callbackContext.success();
    }

    public void setCameraSessionIsPaused(@SuppressWarnings("unused") boolean ignored) {
        callbackContext.error("setCameraSessionIsPaused() is an ios-only method");
    }

    public void permissionRead() {
        if (ActivityCompat.checkSelfPermission(cordova.getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) != 0) {
            ActivityCompat.requestPermissions(cordova.getActivity(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
            callbackContext.error("no permission");
        } else
            callbackContext.success("");
    }

    private IDocumentReaderCompletion getCompletion() {
        return (action, results, error) -> {
            switch (action) {
                case DocReaderAction.COMPLETE:
                    callbackContext.success(JSONConstructor.resultsToJsonObject(results, getContext()).toString());
                    break;
                case DocReaderAction.CANCEL:
                    callbackContext.error("Canceled by user");
                    break;
                case DocReaderAction.ERROR:
                    callbackContext.error("Error: " + error);
                    break;
            }
        };
    }

    private IDocumentReaderPrepareCompletion getPrepareCompletion() {
        return new IDocumentReaderPrepareCompletion() {
            @Override
            public void onPrepareProgressChanged(int progress) {
                if ((int) (System.currentTimeMillis()) > timer + 10) {
                    Log.e("AAA", "Downloading database: " + progress + "%");
                    PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, "Downloading database: " + progress + "%");
                    pluginResult.setKeepCallback(true);
                    callbackContext.sendPluginResult(pluginResult);
                    timer = (int) (System.currentTimeMillis());
                }
            }

            @Override
            public void onPrepareCompleted(boolean status, String error) {
                if (status)
                    callbackContext.success("database prepared");
                else
                    callbackContext.error("database preparation failed: " + error);
            }
        };
    }

    private IDocumentReaderInitCompletion getInitCompletion() {
        return (success, error) -> {
            if (success)
                callbackContext.success("init completed");
            else
                callbackContext.error("Init failed:" + error);
        };
    }

    private Bitmap bitmapFromBase64String(String base64) {
        byte[] decodedString = Base64.decode(base64, Base64.DEFAULT);
        return BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
    }
}