package com.reactlibrary;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import com.epson.epos2.Epos2Exception;
import com.epson.epos2.printer.Printer;
import com.epson.epos2.printer.PrinterStatusInfo;
import com.epson.epos2.printer.ReceiveListener;
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 javax.annotation.Nullable;


import com.facebook.react.bridge.ReadableMap;

import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;

public class EpsonTmModule extends ReactContextBaseJavaModule implements ReceiveListener {

    private final ReactApplicationContext reactContext;
    public static Printer mPrinter = null;
    private Context mContext = null;

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

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

    String connectionSting;
    @ReactMethod
    public void sampleMethod(String stringArgument, int numberArgument, Callback callback) {
        // TODO: Implement some actually useful functionality
        callback.invoke("Received numberArgument: " + numberArgument + " stringArgument: " + stringArgument);
    }

    @ReactMethod
    public void initialSetup(@Nullable ReadableMap dataDic, Callback callback) {
//        Log.d("ADebugTag", "Value: " + dataDic.getString("ipAddress"));

        connectionSting = dataDic.getString("ipAddress");
        if (!initializeObject((int)dataDic.getInt("printerSeries")  , (int)dataDic.getInt("lang"))) {
            callback.invoke("error1","");
        }
        else {
            callback.invoke(null,"done");
        }

    }
    @ReactMethod
    public void addTextToPrint(String stingText, Promise promise) {
        String method = "addText";
        try {
            mPrinter.addText(stingText);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }
    @ReactMethod
    public void addLangToPrint(int lang, Promise promise) {
        String method = "addText";
        try {
            mPrinter.addTextLang(lang);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }
    }
    
    @ReactMethod
    public void addImage(Bitmap bitmap,Integer x,Integer y,Integer width,Integer height,Integer color,Integer mode,Integer halftone,Double brithness,Integer compress, Promise promise) {
        String method = "addImage";
        try {
            mPrinter.addImage(bitmap,x,y,width,height,color,mode,halftone,brithness,compress);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }

    @ReactMethod
    public void addPagePosition(Integer x,Integer y, Promise promise) {
        String method = "addPagePosition";
        try {
            mPrinter.addPagePosition(x,y);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }

    @ReactMethod
    public void addPageArea(Integer x,Integer y,Integer width,Integer height, Promise promise) {
        String method = "addPageArea";
        try {
            mPrinter.addPageArea(x,y,width,height);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }

    @ReactMethod
    public void addPulse(Integer drawer,Integer time, Promise promise) {
        String method = "addPulse";
        try {
            mPrinter.addPulse(drawer,time);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }


    @ReactMethod
    public void addTextSize(Integer width,Integer height,Promise promise) {
        String method = "addTextFont";
        try {
            mPrinter.addTextSize(width,height);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }

    }


    @ReactMethod
    public void addTextFont(Integer font,Promise promise) {
        String method = "addTextFont";
        try {
            mPrinter.addTextFont(font);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }

    }


    @ReactMethod
    public void addTextStyle(Integer stylereverse,Integer ul,Integer em,Integer color,Promise promise) {
        String method = "addTextStyle";
        try {
            mPrinter.addTextStyle(stylereverse,ul,em,color);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }

    }


    @ReactMethod
    public void addFeedLine(Integer line,Promise promise) {
        String method = "addFeedLine";
        try {
            mPrinter.addFeedLine(line);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }

    }


    @ReactMethod
    public void addTextAlign(Integer align,Promise promise) {
        String method = "addTextAlign";
        try {
            mPrinter.addTextAlign(align);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }

    }


    @ReactMethod
    public void addPageEnd(Promise promise) {
        String method = "addPageEnd";
        try {
            mPrinter.addPageEnd();
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }


    @ReactMethod
    public void addPageBegin(Promise promise) {
        String method = "addPageBegin";
        try {
            mPrinter.addPageBegin();
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }

    @ReactMethod
    public void addCut(Promise promise) {
        String method = "addCut";
        try {
            mPrinter.addCut(Printer.CUT_FEED);
            promise.resolve(0);
        }
        catch (Exception e) {
            promise.reject(e);
        }


    }
    @ReactMethod
    public void sendPrint(Callback callback) {
        String printData = printData();
        if (printData != "true") {
            finalizeObject();
            callback.invoke(printData,"");
        }
        else {
            callback.invoke(null, "done");
        }
    }

    private boolean initializeObject(int printerSeries , int lang) {
        try {
            mPrinter = new Printer(printerSeries, lang, mContext);
        } catch (Exception e) {
            ShowMsg.showException(e, "Printer", mContext);
            return false;
        }

        mPrinter.setReceiveEventListener(this);

        return true;
    }

    private void finalizeObject() {
        if (mPrinter == null) {
            return;
        }

        mPrinter.clearCommandBuffer();

        mPrinter.setReceiveEventListener(null);

        mPrinter = null;
    }

    private String connectPrinter() {
        boolean isBeginTransaction = false;

        if (mPrinter == null) {
            return "false";
        }

        try {
            mPrinter.connect(connectionSting, Printer.PARAM_DEFAULT);
        } catch (Exception e) {
//            ShowMsg.showException(e, "connect", mContext);
            return "connection error";
        }

        try {
            mPrinter.beginTransaction();
            isBeginTransaction = true;
        } catch (Exception e) {
//            ShowMsg.showException(e, "beginTransaction", mContext);
            return "connection error";

        }

        if (isBeginTransaction == false) {
            try {
                mPrinter.disconnect();
            } catch (Epos2Exception e) {
                // Do nothing
                return "connection error";
            }
        }

        return "true";
    }

    private String printData() {
        if (mPrinter == null) {
            return "false";
        }
        String connectPrinter = connectPrinter();
        if (connectPrinter != "true") {
            return connectPrinter;
        }

        PrinterStatusInfo status = mPrinter.getStatus();

        dispPrinterWarnings(status);

        if (!isPrintable(status)) {
//            ShowMsg.showMsg(makeErrorMessage(status), mContext);
            try {
                mPrinter.disconnect();
            } catch (Exception ex) {
                // Do nothing
            }
            return makeErrorMessage(status);
        }

        try {
            mPrinter.sendData(Printer.PARAM_DEFAULT);
        } catch (Exception e) {
//            ShowMsg.showException(e, "sendData", mContext);
            try {
                mPrinter.disconnect();
            } catch (Exception ex) {
                // Do nothing
            }
            return "printer error";
        }

        return "true";
    }

    private String makeErrorMessage(PrinterStatusInfo status) {
        String msg = "";

        if (status.getOnline() == Printer.FALSE) {
            msg += reactContext.getString(R.string.handlingmsg_err_offline);
        }
        if (status.getConnection() == Printer.FALSE) {
            msg += reactContext.getString(R.string.handlingmsg_err_no_response);
        }
        if (status.getCoverOpen() == Printer.TRUE) {
            msg += reactContext.getString(R.string.handlingmsg_err_cover_open);
        }
        if (status.getPaper() == Printer.PAPER_EMPTY) {
            msg += reactContext.getString(R.string.handlingmsg_err_receipt_end);
        }
        if (status.getPaperFeed() == Printer.TRUE || status.getPanelSwitch() == Printer.SWITCH_ON) {
            msg += reactContext.getString(R.string.handlingmsg_err_paper_feed);
        }
        if (status.getErrorStatus() == Printer.MECHANICAL_ERR || status.getErrorStatus() == Printer.AUTOCUTTER_ERR) {
            msg += reactContext.getString(R.string.handlingmsg_err_autocutter);
            msg += reactContext.getString(R.string.handlingmsg_err_need_recover);
        }
        if (status.getErrorStatus() == Printer.UNRECOVER_ERR) {
            msg += reactContext.getString(R.string.handlingmsg_err_unrecover);
        }
        if (status.getErrorStatus() == Printer.AUTORECOVER_ERR) {
            if (status.getAutoRecoverError() == Printer.HEAD_OVERHEAT) {
                msg += reactContext.getString(R.string.handlingmsg_err_overheat);
                msg += reactContext.getString(R.string.handlingmsg_err_head);
            }
            if (status.getAutoRecoverError() == Printer.MOTOR_OVERHEAT) {
                msg += reactContext.getString(R.string.handlingmsg_err_overheat);
                msg += reactContext.getString(R.string.handlingmsg_err_motor);
            }
            if (status.getAutoRecoverError() == Printer.BATTERY_OVERHEAT) {
                msg += reactContext.getString(R.string.handlingmsg_err_overheat);
                msg += reactContext.getString(R.string.handlingmsg_err_battery);
            }
            if (status.getAutoRecoverError() == Printer.WRONG_PAPER) {
                msg += reactContext.getString(R.string.handlingmsg_err_wrong_paper);
            }
        }
        if (status.getBatteryLevel() == Printer.BATTERY_LEVEL_0) {
            msg += reactContext.getString(R.string.handlingmsg_err_battery_real_end);
        }

        return msg;
    }

    private boolean isPrintable(PrinterStatusInfo status) {
        if (status == null) {
            return false;
        }

        if (status.getConnection() == Printer.FALSE) {
            return false;
        } else if (status.getOnline() == Printer.FALSE) {
            return false;
        } else {
            ;//print available
        }

        return true;
    }

    private void dispPrinterWarnings(PrinterStatusInfo status) {
        //EditText edtWarnings = (EditText) findViewById(R.id.edtWarnings);
        String warningsMsg = "";

        if (status == null) {
            return;
        }

        if (status.getPaper() == Printer.PAPER_NEAR_END) {
            warningsMsg += reactContext.getString(R.string.handlingmsg_warn_receipt_near_end);
        }

        if (status.getBatteryLevel() == Printer.BATTERY_LEVEL_1) {
            warningsMsg += reactContext.getString(R.string.handlingmsg_warn_battery_near_end);
        }

        //edtWarnings.setText(warningsMsg);
    }

    private boolean createCouponData() {
        String method = "";
        Bitmap coffeeData = BitmapFactory.decodeResource(reactContext.getResources(), R.drawable.coffee);
        Bitmap wmarkData = BitmapFactory.decodeResource(reactContext.getResources(), R.drawable.wmark);

        final int barcodeWidth = 2;
        final int barcodeHeight = 64;
        final int pageAreaHeight = 500;
        final int pageAreaWidth = 500;
        final int fontAHeight = 24;
        final int fontAWidth = 12;
        final int barcodeWidthPos = 110;
        final int barcodeHeightPos = 70;

        if (mPrinter == null) {
            return false;
        }

        try {
            method = "addPageBegin";
            mPrinter.addPageBegin();

            method = "addPageArea";
            mPrinter.addPageArea(0, 0, pageAreaWidth, pageAreaHeight);

            method = "addPageDirection";
            mPrinter.addPageDirection(Printer.DIRECTION_TOP_TO_BOTTOM);

            method = "addPagePosition";
            mPrinter.addPagePosition(0, coffeeData.getHeight());

            method = "addImage";
            mPrinter.addImage(coffeeData, 0, 0, coffeeData.getWidth(), coffeeData.getHeight(), Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT, 3, Printer.PARAM_DEFAULT);

            method = "addPagePosition";
            mPrinter.addPagePosition(0, wmarkData.getHeight());

            method = "addImage";
            mPrinter.addImage(wmarkData, 0, 0, wmarkData.getWidth(), wmarkData.getHeight(), Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT);

            method = "addPagePosition";
            mPrinter.addPagePosition(fontAWidth * 4, (pageAreaHeight / 2) - (fontAHeight * 2));

            method = "addTextSize";
            mPrinter.addTextSize(3, 3);

            method = "addTextStyle";
            mPrinter.addTextStyle(Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT, Printer.TRUE, Printer.PARAM_DEFAULT);

            method = "addTextSmooth";
            mPrinter.addTextSmooth(Printer.TRUE);

            method = "addText";
            mPrinter.addText("FREE Coffee\n");

            method = "addPagePosition";
            mPrinter.addPagePosition((pageAreaWidth / barcodeWidth) - barcodeWidthPos, coffeeData.getHeight() + barcodeHeightPos);

            method = "addBarcode";
            mPrinter.addBarcode("01234567890", Printer.BARCODE_UPC_A, Printer.PARAM_DEFAULT, Printer.PARAM_DEFAULT, barcodeWidth, barcodeHeight);

            method = "addPageEnd";
            mPrinter.addPageEnd();

            method = "addCut";
            mPrinter.addCut(Printer.CUT_FEED);
        } catch (Exception e) {
            ShowMsg.showException(e, method, mContext);
            return false;
        }

        return true;
    }
    private void disconnectPrinter() {
        if (mPrinter == null) {
            return;
        }

        try {
            mPrinter.endTransaction();
        } catch (final Exception e) {
            runOnUiThread(new Runnable() {
                @Override
                public synchronized void run() {
                    ShowMsg.showException(e, "endTransaction", mContext);
                }
            });
        }

        try {
            mPrinter.disconnect();
        } catch (final Exception e) {
            runOnUiThread(new Runnable() {
                @Override
                public synchronized void run() {
                    ShowMsg.showException(e, "disconnect", mContext);
                }
            });
        }

        finalizeObject();
    }

    @Override
    public void onPtrReceive(Printer printer, final int code, final PrinterStatusInfo status, String s) {
        runOnUiThread(new Runnable() {
            @Override
            public synchronized void run() {
//                ShowMsg.showResult(code, makeErrorMessage(status), reactContext);

                dispPrinterWarnings(status);

                //updateButtonState(true);

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        disconnectPrinter();
                    }
                }).start();
            }
        });
    }
}