
package com.regula.documentreader;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.Arguments;

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

import com.regula.documentreader.api.DocumentReader;
import com.regula.documentreader.api.enums.DocReaderAction;
import com.regula.documentreader.api.enums.eRFID_Password_Type;
import com.regula.documentreader.api.enums.eVisualFieldType;
import com.regula.documentreader.api.params.RfidScenario;
import com.regula.documentreader.api.params.ImageInputParam;
import com.regula.documentreader.api.results.DocumentReaderResults;

import static com.regula.documentreader.JSONConstructor.resultsToJsonObject;
import com.regula.documentreader.RfidScenarioAdapter;
import com.regula.documentreader.api.results.DocumentReaderScenario;
import org.json.JSONArray;

public class RNRegulaDocumentReaderModule extends ReactContextBaseJavaModule {

    int timer = -2000000000;
    private final ReactApplicationContext reactContext;
    private final String prepareDatabaseProgressChangeEvent = "prepareDatabaseProgressChangeEvent";

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

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

    @ReactMethod
    public void getScenario(Callback callback) {
        callback.invoke(DocumentReader.Instance().processParams.scenario);
    }

    @ReactMethod
    public void getDocumentReaderIsReady(Callback callback) {
        callback.invoke(DocumentReader.Instance().getDocumentReaderIsReady());
    }

    @ReactMethod
    public void getDocumentReaderStatus(Callback callback) {
        callback.invoke(DocumentReader.Instance().getDocumentReaderStatus());
    }

    @ReactMethod
    public void getCanRFID(Callback callback) {
        callback.invoke(DocumentReader.Instance().getCanRFID());
    }

    @ReactMethod
    public void initialize(ReadableMap opts, final Callback callback) {
        if (!DocumentReader.Instance().getDocumentReaderIsReady()) {
            final byte[] license = Base64.decode(opts.getString("licenseKey"), Base64.NO_WRAP);
            DocumentReader.Instance().initializeReader(reactContext, license, getInitCompletion(callback));
        }else{
            callback.invoke("already initialized");
        }
    }

    @ReactMethod
    public void startNewSession(Callback callback) {
        DocumentReader.Instance().startNewSession();
        callback.invoke();
    }

    @ReactMethod
    public void startNewPage(Callback callback) {
        DocumentReader.Instance().startNewPage();
        callback.invoke();
    }

    @ReactMethod
    public void recognizeImageWithOpts(final ReadableMap opts, String base64Image, final Callback callback) {
        RegulaConfig.setConfig(DocumentReader.Instance(), opts);
        recognizeImage(base64Image, callback);
    }

    @ReactMethod
    public void recognizeImage(String base64Image, final Callback callback) {
        if (DocumentReader.Instance().getDocumentReaderIsReady()) {
            byte[] decodedString = Base64.decode(base64Image, Base64.DEFAULT);
            Bitmap image = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
            DocumentReader.Instance().recognizeImage(image, getCompletion(callback));
        } else {
            callback.invoke("document reader not ready");
        }
    }

    @ReactMethod
    public void recognizeImageFrame(String base64Image, final ReadableMap opts, final Callback callback) {
        if (DocumentReader.Instance().getDocumentReaderIsReady()) {
            byte[] decodedString = Base64.decode(base64Image, Base64.DEFAULT);
            Bitmap image = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
            DocumentReader.Instance().recognizeImageFrame(image, new ImageInputParam(opts.getInt("width"), 
                opts.getInt("height"), opts.getInt("type")), getCompletion(callback));
        } else {
            callback.invoke("document reader not ready");
        }
    }

    @ReactMethod
    public void recognizeVideoFrame(String byteString, final ReadableMap opts, final Callback callback) {
        if (DocumentReader.Instance().getDocumentReaderIsReady()) {
            DocumentReader.Instance().recognizeVideoFrame(byteString.getBytes(), new ImageInputParam(opts.getInt("width"), 
            opts.getInt("height"), opts.getInt("type")), getCompletion(callback));
        } else {
            callback.invoke("document reader not ready");
        }
    }

    @ReactMethod
    public void showScannerWithCameraID(int cameraID, final Callback callback) {
        if (DocumentReader.Instance().getDocumentReaderIsReady()) {
            DocumentReader.Instance().showScanner(cameraID, getCompletion(callback));
        } else {
            callback.invoke("document reader not ready");
        }
    }

    @ReactMethod
    public void showScanner(final Callback callback) {
        showScannerWithCameraID(-1, callback);
    }

    @ReactMethod
    public void showScannerWithCameraIDAndOpts(int cameraID, final ReadableMap opts, final Callback callback) {
        if (DocumentReader.Instance().getDocumentReaderIsReady()) {
            RegulaConfig.setConfig(DocumentReader.Instance(), opts);
            DocumentReader.Instance().showScanner(cameraID, getCompletion(callback));
        } else {
            callback.invoke("document reader not ready");
        }
    }

    @ReactMethod
    public void stopScanner(Callback callback) {
        DocumentReader.Instance().stopScanner();
        callback.invoke();
    }

    @ReactMethod
    public void startRFIDReader(Callback callback) {
        DocumentReader.Instance().startRFIDReader(getCompletion(callback));
    }

    @ReactMethod
    public void stopRFIDReader(Callback callback) {
        DocumentReader.Instance().stopRFIDReader();
        callback.invoke();
    }


    @ReactMethod
    public void prepareDataBase(String dbID, final Callback callback) {
        DocumentReader.Instance().prepareDatabase(reactContext, dbID, getPrepareCompletion(callback));
    }

    @ReactMethod
    public void runAutoUpdate(String dbID, Callback callback) {
        DocumentReader.Instance().runAutoUpdate(reactContext, dbID, getPrepareCompletion(callback));
    }

    @ReactMethod
    public void setRfidScenario(final ReadableMap opts, final Callback callback) {
        RfidScenarioAdapter.setRfidScenario(DocumentReader.Instance().processParams.rfidScenario, opts);
        callback.invoke();
    }

    @ReactMethod
    public void getAPIVersion(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.api);
    }

    @ReactMethod
    public void getCOREVersion(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.core);
    }

    @ReactMethod
    public void getCOREMode(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.coreMode);
    }

    @ReactMethod
    public void getDatabaseID(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.database.databaseID);
    }

    @ReactMethod
    public void getDatabaseVersion(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.database.version);
    }

    @ReactMethod
    public void getDatabaseExportDate(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.database.date);
    }

    @ReactMethod
    public void getSupportedDatabaseDocuments(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.database.databaseDescription);
    }

    @ReactMethod
    public void getNumberOfSupportedDatabaseCountries(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.database.countriesNumber);
    }

    @ReactMethod
    public void getNumberOfSupportedDatabaseDocuments(Callback callback) {
        callback.invoke(DocumentReader.Instance().version.database.documentsNumber);
    }

    @ReactMethod
    public void getAvailableScenarios(Callback callback) {
        JSONArray scenarios = new JSONArray();
        for (DocumentReaderScenario scenario : DocumentReader.Instance().availableScenarios) {
            scenarios.put(scenario.name);
        }
        callback.invoke(scenarios.toString());
    }

    @ReactMethod
    public void getSessionLogFolderPath(Callback callback) {
        callback.invoke(DocumentReader.Instance().processParams.sessionLogFolder);
    }

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

    @ReactMethod
    public void setConfig(final ReadableMap opts, final Callback callback) {
        if (DocumentReader.Instance().getDocumentReaderIsReady()) {
            RegulaConfig.setConfig(DocumentReader.Instance(), opts);
            callback.invoke();
        } else {
            callback.invoke("document reader not ready");
        }
    }

    private DocumentReader.DocumentReaderCompletion getCompletion(final Callback callback) {
        return new DocumentReader.DocumentReaderCompletion() {
            @Override
            public void onCompleted(int action, DocumentReaderResults results, String error) {
                DocumentReader reader = DocumentReader.Instance();
                if (action == DocReaderAction.COMPLETE) {
                    callback.invoke("Success:" + resultsToJsonObject(results).toString());
                } else {
                    if (action == DocReaderAction.CANCEL) {
                        callback.invoke("Canceled by user");
                    } else if (action == DocReaderAction.ERROR) {
                        callback.invoke("Error:" + error);
                    }
                }
            }
        };
    }

    private DocumentReader.DocumentReaderPrepareCompletion getPrepareCompletion(final Callback callback){
        return new DocumentReader.DocumentReaderPrepareCompletion() {
            @Override
            public void onPrepareProgressChanged(int progress) {
                if ((int) (System.currentTimeMillis()) > timer + 10) {
                    // without a timer events are being sent too fast what results in blocking UI and skipping frames
                    WritableMap map = Arguments.createMap();
                    map.putString("msg", "Downloading database: " + progress + "%");
                    reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                            .emit(prepareDatabaseProgressChangeEvent, map);
                    timer = (int) (System.currentTimeMillis());
                }
            }
            @Override
            public void onPrepareCompleted(boolean status, String error) {
                if (status) {
                    callback.invoke("database prepared");
                } else {
                    callback.invoke("database preparation failed: " + error);
                }
            }
        };
    }

    private DocumentReader.DocumentReaderInitCompletion getInitCompletion(final Callback callback){
        return new DocumentReader.DocumentReaderInitCompletion() {
            @Override
            public void onInitCompleted(boolean success, String error) {
                if (success) {
                    DocumentReader.Instance().processParams.scenario = DocumentReader
                            .Instance().availableScenarios.get(0).name;
                    callback.invoke("init completed");
                } else {
                    callback.invoke("Init failed:" + error);
                }
            }
        };
    }
}
