package com.pingcap.fuction;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.ShortByReference;

public class HCICRF {
    private interface hcicrf extends Library {
        int hc_init(short port, int buad);

        int hc_exit(int icdev);

        int getErrMsg(int st, int lg, byte[] byMsg);

        int srd_ver(int icdev, short len, byte[] ver);

        int dv_beep(int icdev, short time);

        int rf_card_7uid(int icdev, byte _Mode, byte[] _Snr);

        int rf_Tag_CheckNdef(int icdev, ByteByReference pTagState);

        int rf_Tag_FormatNdef(int icdev);

        int rf_Tag_ReadNdef(int icdev, byte[] pData, ShortByReference pLength);

        int rf_Tag_SetConfigNdef(int icdev, int wConfig, int wValue);

        int rf_Tag_WriteNdef(int icdev, byte[] pData, int pLength);
    }

    // 64 位 JVM 使用 hc_icrf64, 涉及window 电脑真实地址，
//    private static hcicrf epen = (hcicrf) Native.loadLibrary("lib\\hc_icrf64", hcicrf.class);

    // 北京 office
    private static hcicrf epen = (hcicrf) Native.loadLibrary("C:\\Users\\kenne\\Desktop\\hc_icrf64", hcicrf.class);
    // 美国打印机  -- yuchen.deng
//    private static hcicrf epen = (hcicrf) Native.loadLibrary("C:\\Users\\invic\\Documents\\hc_icrf64", hcicrf.class);
    // 新加坡打印机 -- yangyan
//    private static hcicrf epen = (hcicrf) Native.loadLibrary("C:\\Users\\TiDBSG\\Documents\\hc_icrf64", hcicrf.class);
    //印尼打印机   -- reiza
//    private static hcicrf epen = (hcicrf) Native.loadLibrary("C:\\Users\\vsree\\Documents\\hc_icrf64", hcicrf.class);
    //泰国打印机 -- reiza
//    private static hcicrf epen = (hcicrf) Native.loadLibrary("C:\\Users\\vsree\\Documents\\hc_icrf64", hcicrf.class);
    public static String GetErrMsg(int st) {
        byte[] msg = new byte[128];
        epen.getErrMsg(st, 0, msg);
        try {
            return new String(msg, "GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }


    public int InitReader(short port, int buad) {
        int it = epen.hc_init(port, buad);
        if (it <= 0) {
            System.out.println(GetErrMsg(it));
        } else {
            byte[] ver = new byte[32];
            int st = epen.srd_ver(it, (short) 0, ver);
            if (st == 0) {
                System.out.println(new String(ver));
            } else {
                System.out.println(GetErrMsg(it));
                epen.hc_exit(it);
            }
        }
        return it;
    }

    public void ExitReader(int icdev) {
        int st = epen.hc_exit(icdev);
        if (st != 0) {
            System.out.println(GetErrMsg(st));
        }
    }

    public int WriteNdefTag(int icdev, byte[] ndefData, int ndefDataLen) {
        // Set the card type
        int st = epen.rf_Tag_SetConfigNdef(icdev, 0, 2);
        if (st != 0) {
            System.out.println(icdev + " " + GetErrMsg(st));
            return st;
        }
        System.out.println("rf_Tag_SetConfigNdef successful!");

        // Activate the card
        byte[] snr = new byte[7];
        st = epen.rf_card_7uid(icdev, (byte) 1, snr);
        if (st != 0) {
            System.out.println(icdev + " " + GetErrMsg(st));
            return st;
        }
        System.out.println(icdev + " rf_card_7uid successful! Serial number: " + ReaderUtils.bytesToHexString(snr, 0, 7));

        // Check and format the tag
        ByteByReference tagState = new ByteByReference();
        st = epen.rf_Tag_CheckNdef(icdev, tagState);
        if (st != 0) {
            System.out.println(icdev + " " + GetErrMsg(st));
            return st;
        } else {
            switch (tagState.getValue()) {
                case 0: // If 0, the tag is in the default factory state and needs to be formatted to NDEF
                    st = epen.rf_Tag_FormatNdef(icdev);
                    if (st != 0) {
                        System.out.println(icdev + " " + GetErrMsg(st));
                        return st;
                    }
                    System.out.println("rf_Tag_FormatNdef successful!");
                    break;
                case 1: // If 1, the tag has just been formatted to NDEF
                    System.out.println("The tag is in NDEF initialized state!");
                    break;
                case 2: // If 2, the tag is in read-only state and cannot be modified
                    System.out.println("The tag is read-only!");
                    return -1;
                case 4:
                    System.out.println("The tag is read-write capable!");
                    break;
                default: // If the state is unknown or not handled, return an error
                    System.out.println("Unknown tag state!");
                    return -1;
            }
        }

        // Write NDEF data with retries
        int maxRetries = 3;
        boolean writeSuccess = false;
        for (int retry = 0; retry < maxRetries && !writeSuccess; retry++) {
            st = epen.rf_Tag_WriteNdef(icdev, ndefData, ndefDataLen);
            if (st != 0) {
                System.out.println(icdev + " " + GetErrMsg(st));
                if (retry < maxRetries - 1) {
                    System.out.println("Retrying write operation, attempt " + (retry + 2));
                }
            } else {
                System.out.println("rf_Tag_WriteNdef successful");
                writeSuccess = true;
            }
        }

        if (!writeSuccess) {
            System.out.println("Writing to the tag failed after " + maxRetries + " attempts.");
            return st;
        }

        // Data verification
        byte[] readData = new byte[ndefDataLen];
        ShortByReference readDataLen = new ShortByReference((short) ndefDataLen);

        int readStatus = epen.rf_Tag_ReadNdef(icdev, readData, readDataLen);
        if (readStatus != 0) {
            System.out.println(icdev + " " + GetErrMsg(readStatus));
            return readStatus;
        } else if (Arrays.equals(ndefData, Arrays.copyOf(readData, readDataLen.getValue()))) {
            System.out.println("Data verification successful, writing confirmed!");
            epen.dv_beep(icdev, (short) 30);
        } else {
            System.out.println("Data verification failed, writing not confirmed!");
            return -2;
        }

        return 0;
    }

}
