
package com.homansystem.iposprinter;

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.Promise;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableArray;

import android.util.Base64;

import javax.annotation.Nullable;
import android.widget.Toast;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.content.IntentFilter;
import android.app.Activity;

public class RNReactNativeIposPrinterModule extends ReactContextBaseJavaModule
    implements PrinterEventListener {

  public static final int WIDTH_58 = 384;
  public static final int WIDTH_80 = 576;

  private int deviceWidth = WIDTH_80;

  Promise mPromise;
  MyPrinter printer;
  BTDiscovery discovery;
  KassenBTDiscovery kassenDiscovery;
  private LocalBroadcastReceiver mLocalBroadcastReceiver;
  private LocalKassenBroadcastReceiver mLocalKassenBroadcastReceiver;
  private final ReactApplicationContext reactContext;

  public RNReactNativeIposPrinterModule(ReactApplicationContext reactContext) {
    super(reactContext);
    this.reactContext = reactContext;
    this.mLocalBroadcastReceiver = new LocalBroadcastReceiver(reactContext);
    this.mLocalKassenBroadcastReceiver = new LocalKassenBroadcastReceiver(reactContext);
    LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(reactContext);
    localBroadcastManager.registerReceiver(mLocalBroadcastReceiver, new IntentFilter("BTDeviceFound"));
    localBroadcastManager.registerReceiver(mLocalKassenBroadcastReceiver, new IntentFilter("BTKassenDeviceFound"));
  }

  @Override
  public void onInitializeSuccess(String deviceInfo) {
    if (mPromise != null) {
      mPromise.resolve(deviceInfo);
    }
  }

  @Override
  public void onInitializeError(Error error) {
    if (mPromise != null) {
      mPromise.reject(error.toString());
      Toast.makeText(getReactApplicationContext(), "ERROR:" + error.toString(), Toast.LENGTH_LONG).show();
    }
  }

  @Override
  public void onPrinterClosed(String message) {

  }

  @Override
  public String getName() {
    return "RNReactNativeIposPrinter";
  }

  @ReactMethod
  public void initialize(String tipe, Promise promise) {
    mPromise = promise;
    printer = null;
    Activity mActivity = getCurrentActivity();
    switch (tipe) {
      case "IPPrinter":
        printer = new IPPrinter(getReactApplicationContext(), this);
        break;
      case "BTPrinter":
        printer = new BTPrinter(getReactApplicationContext(), this);
        break;
      case "USBPrinter":
        printer = new USBPrinter(getReactApplicationContext(), this, mActivity);
        break;
      case "HarvardPrinter":
        printer = new HarvardPrinter(getReactApplicationContext(), this, mActivity);
        break;
      case "KassenPrinter":
        printer = new KassenBTPrinter(getReactApplicationContext(), this);
        break;
    }
  }

  @ReactMethod
  public void setDebug(Boolean isDebug) {
    printer.setDebug(isDebug);
  }

  @ReactMethod
  public void hasCutter(Boolean value) {
    printer.hasCutter(value);
  }

  @ReactMethod
  public void initializeBTDiscovery(Promise promise) {
    Activity mActivity = getCurrentActivity();
    discovery = new BTDiscovery(getReactApplicationContext(), mActivity);
    promise.resolve(null);
  }

  @ReactMethod
  public void initializeKassenBTDiscovery(Promise promise) {
    Activity mActivity = getCurrentActivity();
    kassenDiscovery = new KassenBTDiscovery(getReactApplicationContext(), mActivity);
    promise.resolve(null);
  }

  @ReactMethod
  public void startDiscovery(Promise promise) {
    if (discovery != null) {
      discovery.startDiscovery();
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Discovery Service has not initialized"));
    }
  }

  @ReactMethod
  public void startKassenDiscovery(Promise promise) {
    if (kassenDiscovery != null) {
      kassenDiscovery.startDiscovery();
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Discover Service has not initialized"));
    }
  }

  @ReactMethod
  public void stopDiscovery(Promise promise) {
    if (discovery != null) {
      discovery.stopDiscovery();
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Discovery Service has not initialized"));
    }
  }

  @ReactMethod
  public void stopKassenDiscovery(Promise promise) {
    if (kassenDiscovery != null) {
      kassenDiscovery.stopDiscovery();
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Discover Service has not initialized"));
    }
  }

  @ReactMethod
  public void isBluetoothEnabled(Promise promise) {
    if (discovery != null) {
      discovery.isBluetoothEnabled(promise);
    } else {
      promise.reject(new Exception("Discovery Service has not initialized"));
    }
  }

  @ReactMethod
  public void enableBluetooth(Promise promise) {
    if (discovery != null) {
      discovery.enableBluetooth(promise);
    } else {
      promise.reject(new Exception("Discovery Service has not initialized"));
    }
  }

  @ReactMethod
  public void isKassenBluetoothEnabled(Promise promise) {
    if (kassenDiscovery != null) {
      kassenDiscovery.isBluetoothEnabled(promise);
    } else {
      promise.reject(new Exception("Discovery Service has not initialized"));
    }
  }

  @ReactMethod
  public void enableKassenBluetooth(Promise promise) {
    if (kassenDiscovery != null) {
      kassenDiscovery.enableBluetooth(promise);
    } else {
      promise.reject(new Exception("Discovery Service has not initialized"));
    }
  }

  @ReactMethod
  public void connect(String ipAddress, Promise promise) {
    if (printer != null) {
      printer.connect(ipAddress, promise);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void writeTextWProps(String text, @Nullable ReadableMap property, Promise promise) {
    if (printer != null) {
      printer.writeTextWProps(text, property);
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void writeText(String text, Promise promise) {
    if (printer != null) {
      printer.writeText(text);
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void addTextAlign(int align, Promise promise) {
    if (printer != null) {
      printer.addTextAlign(align);
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void writeFeed(int length, Promise promise) {
    if (printer != null) {
      printer.writeFeed(length);
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void writeCut(Promise promise) {
    if (printer != null) {
      printer.writeCut();
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  public Bitmap pad(Bitmap src, int padding_x, int padding_y) {
    Bitmap outputimage = Bitmap.createBitmap(src.getWidth() + padding_x, src.getHeight() + padding_y,
        Bitmap.Config.ARGB_8888);
    Canvas can = new Canvas(outputimage);
    can.drawARGB(255, 255, 255, 255);
    can.drawBitmap(src, padding_x, padding_y, null);
    return outputimage;
  }

  @ReactMethod
  public void writeImage(String base64encodeStr, @Nullable ReadableMap property, Promise promise) {
    int width = 0;
    int leftPadding = 0;
    if (property != null) {
      width = property.hasKey("width") ? property.getInt("width") : 0;
      leftPadding = property.hasKey("left") ? property.getInt("left") : 0;
    }
    if (width > deviceWidth || width == 0) {
      width = deviceWidth;
    }
    byte[] bytes = Base64.decode(base64encodeStr, Base64.DEFAULT);
    Bitmap mBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    if (mBitmap != null) {
      double ratio = (double) mBitmap.getHeight() / (double) mBitmap.getWidth();
      int height = (int) (width * ratio);
      int nMode = 0;
      if (width > 0 && height > 0) {
        Bitmap rBitmap = Bitmap.createScaledBitmap(mBitmap, width, height, true);
        Bitmap fBitmap;
        if (leftPadding > 0) {
          fBitmap = pad(rBitmap, leftPadding, 0);
        } else {
          fBitmap = rBitmap;
        }
        if (printer != null) {
          printer.writeImage(fBitmap, width + leftPadding);
          promise.resolve(null);
        } else {
          promise.reject(new Exception("Printer Service has not initialized"));
        }
      } else {
        promise.reject(String.format(
            "Width and height must be more than 0. Width:%s Height:%s Ratio: %s, Bitmap Width: %s, Bitmap Height: %s",
            Integer.toString(width), Integer.toString(height), Double.toString(ratio),
            Integer.toString(mBitmap.getWidth()), Integer.toString(mBitmap.getHeight())));
      }
    } else {
      promise.resolve(null);
    }
  }

  @ReactMethod
  public void writePulse(Promise promise) {
    if (printer != null) {
      printer.writePulse();
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void printColumn(ReadableArray columnWidths, ReadableArray columnAligns, ReadableArray columnTexts,
      @Nullable ReadableMap options, final Promise promise) {
    if (printer != null) {
      printer.printColumn(columnWidths, columnAligns, columnTexts, options, promise);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void writeBeep(int numOfBeep, int buzzingTime, final Promise promise) {
    if (printer != null) {
      printer.writeBeep(numOfBeep, buzzingTime);
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void startPrint(Promise promise) {
    if (printer != null) {
      printer.startPrint(promise);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

  @ReactMethod
  public void disconnect(Promise promise) {
    if (printer != null) {
      printer.disconnect();
      promise.resolve(null);
    } else {
      promise.reject(new Exception("Printer Service has not initialized"));
    }
  }

}