// PrinterControllerModule.java

package com.nemonicsdk;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
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.ReadableMap;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import com.mangoslab.nemonicsdk.INPrinterControllerCallback;
import com.mangoslab.nemonicsdk.constants.NPrinterType;
import com.mangoslab.nemonicsdk.constants.NPrintQuality;
import com.mangoslab.nemonicsdk.constants.NResult;
import com.mangoslab.nemonicsdk.constants.NResultString;
import com.mangoslab.nemonicsdk.NPrinter;
import com.mangoslab.nemonicsdk.NPrinterController;
import com.mangoslab.nemonicsdk.NPrintInfo;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class NPrinterControllerModule extends ReactContextBaseJavaModule implements INPrinterControllerCallback {
    private static final String TAG = "NPrinterControllerModule";

    private ReactApplicationContext reactContext;

    private NPrinterController printerController = null;

    private NPrinter printer = new NPrinter();

    public NPrinterControllerModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        printerController = new NPrinterController(reactContext, this);
    }

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

    @ReactMethod
    public void getBleConnectDelayOffset(Promise promise) {
        long result = printerController.getBleConnectDelayOffset();
        promise.resolve(result);
    }

    @ReactMethod
    public void setBleConnectDelayOffset(ReadableMap params) {
        int msec = params.getInt("msec");
        printerController.setBleConnectDelayOffset(msec);
    }

    @ReactMethod
    public void getBleSendRetryDelayOffset(Promise promise) {
        int result = printerController.getBleSendRetryDelayOffset();
        promise.resolve(result);
    }

    @ReactMethod
    public void setBleSendRetryDelayOffset(ReadableMap params) {
        int msec = params.getInt("msec");
        printerController.setBleSendRetryDelayOffset(msec);
    }

    @ReactMethod
    public void getDefaultConnectDelay(Promise promise) {
        promise.resolve(0);
    }

    @ReactMethod
    public void getConnectDelay(Promise promise) {
        promise.resolve(0);
    }

    @ReactMethod
    public void setConnectDelay(ReadableMap params) {
    }

    @ReactMethod
    public void connect(ReadableMap params, Promise promise) {
        // MGLog.e(TAG, "connect start");
        String name = params.getString("name");
        String macAddress = params.getString("macAddress");
        int type = params.getInt("type");

        NPrinter printer = new NPrinter();
        printer.setName(name);
        printer.setMacAddress(macAddress);
        printer.setType(NPrinterType.valueOf(type));
        int result = printerController.connect(printer);

        if (result == NResult.OK)
            this.printer = printer;
            
        // MGLog.e(TAG, "connect end %d", result);

        promise.resolve(result);
    }

    @ReactMethod
    public void disconnect() {
        printerController.disconnect();
        printer = null;
    }

    @ReactMethod
    public void getConnectState(Promise promise) {
        int result = printerController.getConnectState();
        promise.resolve(result);
    }

    @ReactMethod
    public void cancel() {
        printerController.cancel();
    }

    @ReactMethod
    public void setPrintTimeout(ReadableMap params) {
        boolean enableAuto = params.getBoolean("enableAuto");
        int manualTime = params.getInt("manualTime");

        printerController.setPrintTimeout(enableAuto, manualTime);
    }

    @ReactMethod
    public void print(ReadableMap params, Promise promise) {
        Log.d(TAG, "print start");
        // MGLog.e(TAG, "print start");
        
        ReadableArray imageData = params.getArray("images");
        ReadableArray imageUrls = params.getArray("imageUrls");

        Log.d(TAG, "print 1");
        // MGLog.e(TAG, "print 1");

        NPrintQuality quality = NPrintQuality.valueOf(params.getInt("quality"));
        int copies = params.getInt("copies");
        boolean isLastPageCut = params.getBoolean("isLastPageCut");
        boolean enableDither = params.getBoolean("enableDither");
        boolean isCheckPrinterStatus = params.getBoolean("isCheckPrinterStatus");
        boolean isCheckCartridgeType = params.getBoolean("isCheckCartridgeType");
        boolean isCheckPower = params.getBoolean("isCheckPower");

        Log.d(TAG, "print 2");
        // MGLog.e(TAG, "print 2");

        List<Bitmap> images = getImages(imageData, imageUrls);
        NPrintInfo printInfo = new NPrintInfo(printer, images);
        printInfo.setPrintQuality(quality)
            .setCopies(copies)
            .setEnableLastPageCut(isLastPageCut)
            .setEnableDither(enableDither)
            .setEnableCheckPrinterStatus(isCheckPrinterStatus)
            .setEnableCheckCartridgeType(isCheckCartridgeType)
            .setEnableCheckPower(isCheckPower);

        Log.d(TAG, String.format("print 3 images size = %d", images.size()));
        // MGLog.e(TAG, "print 3 images size = %d", images.size());

        int result = printerController.print(printInfo);
        promise.resolve(result);

        Log.d(TAG, String.format("print end result %d", result));
        // MGLog.e(TAG, "print end result %d", result);
    }

    private List<Bitmap> getImages(ReadableArray imageData, ReadableArray imageUrls) {
        List<Bitmap> result = new ArrayList<>();

        if (imageData != null && imageData.size() > 0) {
            for (int i=0; i<imageData.size(); i++) {
                ReadableArray imageArray = imageData.getArray(i);
                
                int size = imageArray.size();
                byte[] data = new byte[size];
                for (int j=0; j<size; j++) {
                    data[j] = (byte)(imageArray.getInt(j) & 0xff);
                }

                Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length);
                result.add(image);
            }
        }
        else {
            for (int i=0; i<imageUrls.size(); i++) {
                String imageUrl = imageUrls.getString(i);

                try {
                    URL url = new URL(imageUrl);
                    Bitmap image = BitmapFactory.decodeStream(url.openStream());
                    result.add(image);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        return result;
    }

    @ReactMethod
    public void setTemplate(ReadableMap params, Promise promise) {
        ReadableArray imageArray = params.getArray("image");
        boolean withPrint = params.getBoolean("withPrint");
        boolean enableDither = params.getBoolean("enableDither");

        int size = imageArray.size();
        byte[] data = new byte[size];
        for (int i=0; i<size; i++) {
            data[i] = (byte)(imageArray.getInt(i) & 0xff);
        }
        Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length);

        int result = printerController.setTemplate(image, withPrint, enableDither);
        promise.resolve(result);
    }

    @ReactMethod
    public void setTemplateWithUrl(ReadableMap params, Promise promise) {
        String imageUrl = params.getString("imageUrl");
        boolean withPrint = params.getBoolean("withPrint");
        boolean enableDither = params.getBoolean("enableDither");

        try {
            URL url = new URL(imageUrl);
            Bitmap image = BitmapFactory.decodeStream(url.openStream());

            int result = printerController.setTemplate(image, withPrint, enableDither);
            promise.resolve(result);
        } catch (Exception e) {
            e.printStackTrace();
            promise.resolve(NResult.INVALID_PARAMETER);
        }
    }

    @ReactMethod
    public void clearTemplate(Promise promise) {
        int result = printerController.clearTemplate();
        promise.resolve(result);
    }

    @ReactMethod
    public void getPrinterStatus(Promise promise) {
        int result = printerController.getPrinterStatus();
        promise.resolve(result);
    }

    @ReactMethod
    public void getCartridgeType(Promise promise) {
        int result = printerController.getCartridgeType();
        promise.resolve(result);
    }

    @ReactMethod
    public void getPrinterName(Promise promise) {
        NResultString result = printerController.getPrinterName();

        WritableMap resultParams = Arguments.createMap();
        resultParams.putInt("result", result.getResult());
        resultParams.putString("value", result.getValue());
        promise.resolve(resultParams);
    }

    @ReactMethod
    public void getBatteryLevel(Promise promise) {
        int result = printerController.getBatteryLevel();
        promise.resolve(result);
    }

    @ReactMethod
    public void getBatteryStatus(Promise promise) {
        int result = printerController.getBatteryStatus();
        promise.resolve(result);
    }

    @Override
    public void disconnected() {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("disconnected", null);
    }

    @Override
    public void printProgress(int index, int total, int result) {
        WritableMap params = Arguments.createMap();
        params.putInt("index", index);
        params.putInt("total", total);
        params.putInt("result", result);
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("printProgress", params);
    }

    @Override
    public void printComplete(int result) {
        WritableMap params = Arguments.createMap();
        params.putInt("result", result);
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("printComplete", params);
    }
}
