package com.reactnativethermalprint.async;

import android.content.Context;
import android.os.AsyncTask;
import com.reactnativethermalprint.escposprinter.EscPosCharsetEncoding;
import com.reactnativethermalprint.escposprinter.EscPosPrinter;
import com.reactnativethermalprint.escposprinter.connection.DeviceConnection;
import com.reactnativethermalprint.escposprinter.exceptions.EscPosBarcodeException;
import com.reactnativethermalprint.escposprinter.exceptions.EscPosConnectionException;
import com.reactnativethermalprint.escposprinter.exceptions.EscPosEncodingException;
import com.reactnativethermalprint.escposprinter.exceptions.EscPosParserException;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.lang.ref.WeakReference;
import javax.annotation.Nullable;

public abstract class AsyncEscPosPrint extends AsyncTask<AsyncEscPosPrinter, Double, Integer> {
    protected final static int FINISH_SUCCESS = 1;
    protected final static int FINISH_NO_PRINTER = 2;
    protected final static int FINISH_PRINTER_DISCONNECTED = 3;
    protected final static int FINISH_PARSER_ERROR = 4;
    protected final static int FINISH_ENCODING_ERROR = 5;
    protected final static int FINISH_BARCODE_ERROR = 6;

    protected final static double PROGRESS_CONNECTING = 1;
    protected final static double PROGRESS_CONNECTED = 2;
    protected final static double PROGRESS_PRINTING = 3;
    protected final static double PROGRESS_PRINTED = 4;

    protected WeakReference<Context> weakContext;
    protected ReactContext reactContext;
    protected Configs configs;
    protected CallbackPrint callbackPrint;



  public AsyncEscPosPrint(Context context, ReactContext reactContext, Configs configs, CallbackPrint callbackPrint) {
        this.weakContext = new WeakReference<>(context);
        this.reactContext = reactContext;
        this.configs = configs;
        this.callbackPrint = callbackPrint;

    }

    protected Integer doInBackground(AsyncEscPosPrinter... printersData) {
        if (printersData.length == 0) {
            return AsyncEscPosPrint.FINISH_NO_PRINTER;
        }

        this.publishProgress(AsyncEscPosPrint.PROGRESS_CONNECTING);

        AsyncEscPosPrinter printerData = printersData[0];

        try {
            DeviceConnection deviceConnection = printerData.getPrinterConnection();

            if(deviceConnection == null) {
                return AsyncEscPosPrint.FINISH_NO_PRINTER;
            }
            this.publishProgress(AsyncEscPosPrint.PROGRESS_CONNECTED);
            EscPosPrinter printer = new EscPosPrinter(
                    deviceConnection,
                    printerData.getPrinterDpi(),
                    printerData.getPrinterWidthMM(),
                    printerData.getPrinterNbrCharactersPerLine(),
                    new EscPosCharsetEncoding("windows-1252", 16)
            );

            this.publishProgress(AsyncEscPosPrint.PROGRESS_PRINTING);
            if(printersData[1] == null){

              if (this.configs.getOpenCashbox()) {
                printer.printFormattedTextAndOpenCashBox(printerData.getTextToPrint(), this.configs.getMmFeedPaper());
              } else if (this.configs.getAutoCut()) {
                printer.printFormattedTextAndCut(printerData.getTextToPrint(), this.configs.getMmFeedPaper());
              } else {
                printer.printFormattedText(printerData.getTextToPrint(), this.configs.getMmFeedPaper());
              }
              if(this.configs.getSpeed() != 0){
                String[] array = printerData.getTextToPrint().split("\n", -1);
                for (int i = 0; i < array.length; i++) {
                  double progress =  ((1 / (float) array.length) * i) +  AsyncEscPosPrint.PROGRESS_PRINTING;
                  this.publishProgress(progress);
                  Thread.sleep(this.configs.getSpeed());
                  
                }
              }
             
            } else {
              printer.printFormattedText(printerData.getTextToPrint(), 1f);
              if(this.configs.getSpeed() != 0){
                String[] array = printerData.getTextToPrint().split("\n", -1);
                for (int i = 0; i < array.length; i++) {
                  double progress =  ((1 / (float) array.length) * i) +  AsyncEscPosPrint.PROGRESS_PRINTING;
                  this.publishProgress(progress);
                  Thread.sleep(this.configs.getSpeed());
                  
                }
              }
              if (this.configs.getOpenCashbox()) {
                printer.printFormattedTextAndOpenCashBox(printersData[1].getTextToPrint(), this.configs.getMmFeedPaper());
              } else if (this.configs.getAutoCut()) {
                printer.printFormattedTextAndCut(printersData[1].getTextToPrint(), this.configs.getMmFeedPaper());
              } else {
                printer.printFormattedText(printersData[1].getTextToPrint(), this.configs.getMmFeedPaper());
              }
            }
            if (this.configs.getDisconnectWhenFinished()) printer.disconnectPrinter();

            this.publishProgress(AsyncEscPosPrint.PROGRESS_PRINTED);

        } catch (EscPosConnectionException e) {
            e.printStackTrace();
            return AsyncEscPosPrint.FINISH_PRINTER_DISCONNECTED;
        } catch (EscPosParserException e) {
            e.printStackTrace();
            return AsyncEscPosPrint.FINISH_PARSER_ERROR;
        } catch (EscPosEncodingException e) {
            e.printStackTrace();
            return AsyncEscPosPrint.FINISH_ENCODING_ERROR;
        } catch (EscPosBarcodeException e) {
            e.printStackTrace();
            return AsyncEscPosPrint.FINISH_BARCODE_ERROR;
        } catch (InterruptedException e) {
          e.printStackTrace();
        }

      return AsyncEscPosPrint.FINISH_SUCCESS;
    }

    protected void onPreExecute() {
        Context context = weakContext.get();

        if (context == null) {
            return;
        }

        WritableMap params = Arguments.createMap();
        params.putInt("progress", 0);
        this.sendProgress(params);

    }

    protected void onProgressUpdate(Double... progress) {
        WritableMap params = Arguments.createMap();
        params.putDouble("progress", progress[0]);
        this.sendProgress(params);

    }

    protected void onPostExecute(Integer result) {

        WritableMap params = Arguments.createMap();
        Context context = weakContext.get();

        if (context == null) {
            return;
        }

        switch (result) {
            case AsyncEscPosPrint.FINISH_SUCCESS:
                this.callbackPrint.resolve();
                break;
            case AsyncEscPosPrint.FINISH_NO_PRINTER:
                params.putInt("code", AsyncEscPosPrint.FINISH_NO_PRINTER);
                params.putString("message", "The application can't find any printer connected.");
                this.callbackPrint.rejected(params);
                break;
            case AsyncEscPosPrint.FINISH_PRINTER_DISCONNECTED:
                params.putInt("code", AsyncEscPosPrint.FINISH_PRINTER_DISCONNECTED);
                params.putString("message", "Unable to connect the printer.");
                this.callbackPrint.rejected(params);
                break;
            case AsyncEscPosPrint.FINISH_PARSER_ERROR:
                params.putInt("code", AsyncEscPosPrint.FINISH_PARSER_ERROR);
                params.putString("message", "It seems to be an invalid syntax problem.");
                this.callbackPrint.rejected(params);
                break;
            case AsyncEscPosPrint.FINISH_ENCODING_ERROR:
                params.putInt("code", AsyncEscPosPrint.FINISH_ENCODING_ERROR);
                params.putString("message", "The selected encoding character returning an error.");
                this.callbackPrint.rejected(params);
                break;
            case AsyncEscPosPrint.FINISH_BARCODE_ERROR:
                params.putInt("code", AsyncEscPosPrint.FINISH_BARCODE_ERROR);
                params.putString("message", "Data send to be converted to barcode or QR code seems to be invalid.");
                this.callbackPrint.rejected(params);
                break;
        }
    }


    private void sendProgress(@Nullable WritableMap params) {
      this.reactContext
        .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
        .emit("progress", params);
    }
}
