package com.appstrax.cordova.plugin;

import org.apache.cordova.CallbackContext;

import android.app.PendingIntent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;

import android.content.Intent;
import android.content.Context;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;

import java.util.concurrent.ExecutorService;

import java.util.HashMap;
import java.util.Iterator;

import com.appstrax.cordova.plugin.OTGDevice;
import com.appstrax.cordova.plugin.DriverService;
import com.appstrax.cordova.plugin.ReaderModule;
import com.appstrax.cordova.plugin.PL2303Driver;
import com.appstrax.cordova.plugin.RunnableService;

public class UsbService {
    private static final String ACTION_USB_PERMISSION = "com.appstrax.cordova.plugin.USB_PERMISSION";
    private CallbackContext mCallbackContext;
    private Context mContext;
    private MessageReceiver mMessageReceiver;
    private ExecutorService mThreadPool;
    private RunnableService mRunnableService = new RunnableService();
    private UsbManager mUsbManager;

    UsbService(Context context, ExecutorService threadPool) {
        mContext = context;
        mThreadPool = threadPool;
        mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        mMessageReceiver = new MessageReceiver();

        context.registerReceiver(mMessageReceiver, new IntentFilter(ACTION_USB_PERMISSION), Context.RECEIVER_EXPORTED);
        context.registerReceiver(mMessageReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED), Context.RECEIVER_EXPORTED);
        context.registerReceiver(mMessageReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED), Context.RECEIVER_EXPORTED);
    }

    public void connectAndScan(CallbackContext callbackContext) {
        mCallbackContext = callbackContext;

        Iterator<UsbDevice> deviceIterator = getUsbDevices();
        if (deviceIterator.hasNext()) {
            UsbDevice device = deviceIterator.next();
            if (isPL2303Device(device)) {
                if (mUsbManager.hasPermission(device)) {
                    startScanningDevice();
                } else {
                    Intent intent = new Intent(ACTION_USB_PERMISSION);
                    intent.setPackage(mContext.getPackageName());
                    PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE);
                    mUsbManager.requestPermission(device, pi);
                }
            } else {
                callbackContext.error("Invalid USB device found");
            }
        } else {
            callbackContext.error("No USB device found");
        }
    }

    private Iterator<UsbDevice> getUsbDevices() {
        HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
        return deviceList.values().iterator();
    }

    private boolean isPL2303Device(UsbDevice usbDevice) {
        OTGDevice device = new OTGDevice(usbDevice);
        int vid = Integer.parseInt(device.getVendorId(), 16);
        int pid = Integer.parseInt(device.getProductId(), 16);
        int moduleType = ReaderModule.checkOTGModule(vid, pid);
        return moduleType == ReaderModule.CHIP_PL2303;
    }

    private void startScanningDevice() {
        PL2303Driver pL2303Driver = new PL2303Driver(mUsbManager, mContext, ACTION_USB_PERMISSION);

        if (pL2303Driver.enumerate()) {
            DriverService.loadDefaultSettingValues(pL2303Driver, mContext);
            mRunnableService.runDataReceived(pL2303Driver, mThreadPool, mCallbackContext);
        } else {
            System.out.println("Cannot connect to PL2303 Driver");
        }
    }

    public void stopScanning() {
        mRunnableService.stopReadWrite();
    }

    public class MessageReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case ACTION_USB_PERMISSION:
                    UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if (device != null) {
                            startScanningDevice();
                        } else {
                            System.out.println("Cannot connect to PL2303 Driver");
                        }
                    } else {
                        System.out.println("Cannot connect to PL2303 Driver");
                    }
                    break;

                case UsbManager.ACTION_USB_DEVICE_DETACHED:
                    stopScanning();
                    mCallbackContext.error("Device disconnected");
                    break;
            }
        }
    }
}
