package cc.lotuscard;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;

import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;

/**
 * extend LocatusCardPlugin to javascript.
 */
public class LotusCardPlugin extends CordovaPlugin {

    private LotusCardDriver mLotusCardDriver;

    private UsbManager m_UsbManager = null;
    private UsbDevice m_LotusCardDevice = null;
    private UsbInterface m_LotusCardInterface = null;
    private UsbDeviceConnection m_LotusCardDeviceConnection = null;
    private final int m_nVID = 1306;
    private final int m_nPID = 20763;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";

    private Boolean m_bUseUsbHostApi = true;
    private Boolean m_bCanUseUsbHostApi = true;
    private String m_strDeviceNode;

    private int m_nDeviceHandle = -1;

    @Override
    protected void pluginInitialize() {
        Context m_Context = this.cordova.getActivity().getApplicationContext();
        m_UsbManager = (UsbManager) m_Context.getSystemService(Context.USB_SERVICE);
        mLotusCardDriver = new LotusCardDriver();
    }

    @Override
    public void onDestroy() {
        if (-1 != m_nDeviceHandle) mLotusCardDriver.CloseDevice(m_nDeviceHandle);
    }

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if (action.equals("echo")) {
            String message = args.getString(0);
            this.echo(message, callbackContext);
            return true;
        } 
        if (action.equals("card")) {
            Boolean message = args.getBoolean(0);
            this.getCardNo(message, callbackContext);
            return true;
        }  
        if (action.equals("usb")) {
            String message = args.getString(0);
            this.enableUSB(message, callbackContext);
            return true;
        }
        return false;
    }

    private void echo(String message, CallbackContext callbackContext) {
        if (message != null && message.length() > 0) {
            callbackContext.success(message);
        } else {
            callbackContext.error("Expected one non-empty string argument.");
        }
    }

    private void getCardNo(Boolean message, CallbackContext callbackContext) {   
        if (m_nDeviceHandle == -1) {
            m_nDeviceHandle = mLotusCardDriver.OpenDevice("", 0, 0, 0, 0, true);
        }
        if (m_nDeviceHandle != -1) {
            testGetCardNo(m_nDeviceHandle, message, callbackContext);
        } else {
            callbackContext.error("Open Device False!");
        }
    }

    private void testGetCardNo(long nDeviceHandle, Boolean message, CallbackContext callbackContext) {
        boolean bResult = false;
        String strResult = "";

        int nRequestType;
        int nCount = 0;
        long lCardNo = 0;
        int nErrorCode = 0;
        LotusCardParam tLotusCardParam1 = new LotusCardParam();
        nRequestType = LotusCardDriver.RT_NOT_HALT;
        bResult = mLotusCardDriver.GetCardNo(m_nDeviceHandle, nRequestType, tLotusCardParam1);

        if (!bResult) {
            nErrorCode = mLotusCardDriver.GetErrorCode(nDeviceHandle);
            strResult = "Call GetCardNoEx Error!" + GetErrorInfo(nErrorCode);

            callbackContext.error(strResult);
        } else {
            lCardNo = bytes2long(tLotusCardParam1.arrCardNo);
            strResult += "Call GetCardNoEx Ok!";
            strResult += "CardNo(DEC):" + lCardNo;
            strResult += "CardNo(HEX):"
                + leftString(
                        "00"
                                + Integer
                                        .toHexString(tLotusCardParam1.arrCardNo[3]),
                        2).toUpperCase()
                + leftString(
                        "00"
                                + Integer
                                        .toHexString(tLotusCardParam1.arrCardNo[2]),
                        2).toUpperCase()
                + leftString(
                        "00"
                                + Integer
                                        .toHexString(tLotusCardParam1.arrCardNo[1]),
                        2).toUpperCase()
                + leftString(Integer.toHexString(tLotusCardParam1.arrCardNo[0]),
                        2).toUpperCase();

            mLotusCardDriver.Beep(m_nDeviceHandle, 10);
            if (message) mLotusCardDriver.Halt(m_nDeviceHandle);
            callbackContext.success(strResult);
        }

    }

    private void enableUSB(String message, CallbackContext callbackContext) {
        Boolean usbOK = this._usb();
        if (usbOK) {
            callbackContext.success("Find L1-U LotusSmart IC Reader! Device Node:" + m_strDeviceNode);
        } else {
            callbackContext.error(m_strDeviceNode);
        }

    }

    private Boolean _usb() {
        Boolean bResult = false;
        PendingIntent pendingIntent;
        pendingIntent = PendingIntent.getBroadcast(this.cordova.getActivity(), 0, new Intent(ACTION_USB_PERMISSION), 0);

        if (null == m_UsbManager) {
            m_strDeviceNode = "m_UsbManager not exist";
            return bResult;
        }

        HashMap<String, UsbDevice> deviceList = m_UsbManager.getDeviceList();
        if (!deviceList.isEmpty()) {
            for (UsbDevice device : deviceList.values()) {
                if ((m_nVID == device.getVendorId())
                        && (m_nPID == device.getProductId())) {
                    m_LotusCardDevice = device;
                    m_strDeviceNode = m_LotusCardDevice.getDeviceName();
                    break;
                }
            }
        }
        if (null == m_LotusCardDevice) {
            m_strDeviceNode = "m_LotusCardDevice not found";
            return bResult;
        }
        m_LotusCardInterface = m_LotusCardDevice.getInterface(0);
        if (null == m_LotusCardInterface) {
            m_strDeviceNode = "m_strDeviceNode not exist";
            return bResult;
        }
        if (false == m_UsbManager.hasPermission(m_LotusCardDevice)) {
            m_UsbManager.requestPermission(m_LotusCardDevice, pendingIntent);
        }
        UsbDeviceConnection conn = null;
        if (m_UsbManager.hasPermission(m_LotusCardDevice)) {
            conn = m_UsbManager.openDevice(m_LotusCardDevice);
        }

        if (null == conn) {
            m_strDeviceNode = "do not have Permission";
            return bResult;
        }

        if (conn.claimInterface(m_LotusCardInterface, true)) {
            m_LotusCardDeviceConnection = conn;
        } else {
            conn.close();
        }
        if (null == m_LotusCardDeviceConnection) {
            m_strDeviceNode = "m_LotusCardDeviceConnection not exist";
            return bResult;
        }
        // 把上面获取的对性设置到接口中用于回调操作
        LotusCardDriver.m_UsbDeviceConnection = m_LotusCardDeviceConnection;
        if (m_LotusCardInterface.getEndpoint(1) != null) {
            LotusCardDriver.m_OutEndpoint = m_LotusCardInterface.getEndpoint(1);
        }
        if (m_LotusCardInterface.getEndpoint(0) != null) {
            LotusCardDriver.m_InEndpoint = m_LotusCardInterface.getEndpoint(0);
        }
        bResult = true;
        return bResult;
    }

    public long bytes2long(byte[] byteNum) {
        long num = 0;
        for (int ix = 3; ix >= 0; --ix) {
            num <<= 8;
            if (byteNum[ix] < 0) {
                num |= (256 + (byteNum[ix]) & 0xff);
            } else {
                num |= (byteNum[ix] & 0xff);
            }
        }
        return num;
    }

    public String leftString(String strText, int nLeftLength) {
        if(1== strText.length()) strText ="0" +strText;
        if (strText.length() <= nLeftLength)
            return strText;

        return strText.substring(strText.length() - nLeftLength,
                strText.length());
    }

    private String GetErrorInfo(int nErrorCode) {
        String strResult = "未知的错误";
        if (LotusCardErrorCode.LCEC_SEND_COMMAND.ordinal() < nErrorCode)
            return strResult;
        switch (LotusCardErrorCode.values()[nErrorCode]) {
        case LCEC_OK:
            strResult = "正常执行";
            break;
        case LCED_UNKNOWN: // 未知的
            strResult = "未知的错误";
            break;
        case LCEC_SEND_FALSE: // 发送失败
            strResult = "发送失败";
            break;
        case LCEC_RECV_TIME_OUT: // 接收超时
            strResult = "接收超时";
            break;
        case LCEC_RECV_ZERO_LEN: // 接收长度为0
            strResult = "接收长度为0";
            break;
        case LCEC_RECV_CRC_FALSE: // 接收校验失败
            strResult = "接收校验失败";
            break;
        case LCEC_REQUEST: // 寻卡
            strResult = "寻卡错误";
            break;
        case LCEC_ANTICOLL: // 防冲突
            strResult = "防冲突错误";
            break;
        case LCEC_SELECT: // 选卡
            strResult = "选卡错误";
            break;
        case LCEC_AUTHENTICATION: // 三次验证
            strResult = "三次验证错误";
            break;
        case LCEC_HALT: // 中止
            strResult = "中止错误";
            break;
        case LCEC_READ: // 读
            strResult = "读错误";
            break;
        case LCEC_WRITE: // 写
            strResult = "写错误";
            break;
        case LCEC_INCREMENT: // 加值
            strResult = "加值错误";
            break;
        case LCEC_DECREMENT: // 减值
            strResult = "减值错误";
            break;
        case LCEC_LOADKEY: // 装载密码
            strResult = "装载密码错误";
            break;
        case LCEC_BEEP: // 蜂鸣
            strResult = "蜂鸣错误";
            break;
        case LCEC_RESTORE: // 卡数据块传入卡的内部寄存器
            strResult = "数据块传入卡的内部寄存器错误";
            break;
        case LCEC_TRANSFER: // 内部寄存器传入卡的卡数据块
            strResult = "内部寄存器传入卡的卡数据块错误";
            break;
        case LCEC_SEND_COMMAND: // 发送14443指令
            strResult = "发送14443指令错误";
            break;
        default:
            strResult = "未知的错误编码";
            break;

        }

        return strResult;
    }

}