/**
 * AIWI USB Security Token - Java Demo
 * =====================================
 * ตัวอย่างการเชื่อมต่อและส่ง/รับข้อมูลกับ AIWI ด้วยภาษา Java
 *
 * Platform: Linux / Windows / macOS (JDK 8+)
 * Date: 2025
 *
 * ความต้องการ (Maven):
 *   <dependency>
 *       <groupId>org.hid4java</groupId>
 *       <artifactId>hid4java</artifactId>
 *       <version>0.8.0</version>
 *   </dependency>
 *
 * หรือ Gradle:
 *   implementation 'org.hid4java:hid4java:0.8.0'
 *
 * สำหรับ Linux อาจต้องติดตั้ง hidapi ก่อน:
 *   sudo apt install libhidapi-dev
 *
 * Compile & Run:
 *   javac -cp hid4java-0.8.0.jar AIWIDemo.java
 *   java  -cp .:hid4java-0.8.0.jar AIWIDemo
 */

import org.hid4java.HidDevice;
import org.hid4java.HidManager;
import org.hid4java.HidServices;
import org.hid4java.HidServicesSpecification;

public class AIWIDemo {

    // ============================================================
    //  ค่าคงที่
    // ============================================================
    static final int    AIWI_VID    = 0x20A0;  // Vendor ID (ค่า default — อาจถูก customize ได้ใน EEPROM)
    static final int    AIWI_PID    = 0x413A;  // Product ID
    static final int    DATA_SIZE   = 32;      // ขนาด payload จริง (ไม่รวม Report ID)
    static final byte   REPORT_ID   = 0x00;    // HID Report ID (คงที่ 0x00 สำหรับ AIWI)

    // ============================================================
    //  ฟังก์ชัน utility
    // ============================================================

    /**
     * ส่งข้อมูล 32 bytes ไปยัง AIWI (HID SET_REPORT / Feature Report)
     *
     * Protocol:
     *   - hid4java จัดการ Report ID แยกต่างหากผ่าน parameter
     *   - data[] ขนาด 32 bytes คือ payload จริง (ไม่รวม Report ID)
     *
     * @param device  HidDevice object ที่เปิดไว้แล้ว
     * @param data    byte array ขนาด 32 bytes (คำสั่งหรือ payload)
     * @return จำนวน bytes ที่ส่งสำเร็จ หรือ -1 ถ้าผิดพลาด
     */
    static int aiwiSend(HidDevice device, byte[] data) {
        if (data.length != DATA_SIZE) {
            System.err.printf("[ERROR] data ต้องมีขนาด %d bytes (ได้รับ %d bytes)%n",
                              DATA_SIZE, data.length);
            return -1;
        }
        int result = device.sendFeatureReport(data, REPORT_ID);
        if (result < 0) {
            System.err.println("[ERROR] ส่งข้อมูลไม่สำเร็จ: " + device.getLastErrorMessage());
        }
        return result;
    }

    /**
     * รับข้อมูล 32 bytes จาก AIWI (HID GET_REPORT / Feature Report)
     *
     * @param device  HidDevice object ที่เปิดไว้แล้ว
     * @return byte array ขนาด 32 bytes หรือ null ถ้าผิดพลาด
     */
    static byte[] aiwiReceive(HidDevice device) {
        byte[] buf = new byte[DATA_SIZE];
        int result = device.getFeatureReport(buf, REPORT_ID);
        if (result < 0) {
            System.err.println("[ERROR] รับข้อมูลไม่สำเร็จ: " + device.getLastErrorMessage());
            return null;
        }
        return buf;
    }

    /**
     * อ่านข้อมูลจาก AIWI
     *
     * Protocol (2 ขั้นตอน):
     *   1. ส่งคำสั่ง "RDBLxx" ที่เข้ารหัสด้วย AES-256 แล้ว  (SET_REPORT)
     *   2. รับข้อมูลตอบกลับ 32 bytes                         (GET_REPORT)
     *
     * @param device        HidDevice object
     * @param encryptedCmd  คำสั่ง "RDBLxx" ที่ผ่าน AES-256 ECB encryption ด้วย Owner Key แล้ว (32 bytes)
     * @return ข้อมูล 32 bytes หรือ null ถ้าผิดพลาด
     */
    static byte[] readBlock(HidDevice device, byte[] encryptedCmd) {
        System.out.println(">>> ส่งคำสั่ง READ...");
        if (aiwiSend(device, encryptedCmd) < 0) return null;

        System.out.println(">>> รับข้อมูล...");
        return aiwiReceive(device);
    }

    /**
     * เขียนข้อมูลลงใน AIWI
     *
     * Protocol (2 ขั้นตอน):
     *   1. ส่งคำสั่ง "WRBLxx" ที่เข้ารหัสด้วย AES-256 แล้ว  (SET_REPORT)
     *   2. ส่งข้อมูลจริง 32 bytes ที่ต้องการเขียน            (SET_REPORT)
     *
     * @param device        HidDevice object
     * @param encryptedCmd  คำสั่ง "WRBLxx" ที่ผ่าน AES-256 ECB encryption แล้ว (32 bytes)
     * @param data          ข้อมูล 32 bytes ที่ต้องการเขียน
     * @return true ถ้าสำเร็จ
     */
    static boolean writeBlock(HidDevice device, byte[] encryptedCmd, byte[] data) {
        System.out.println(">>> ส่งคำสั่ง WRITE...");
        if (aiwiSend(device, encryptedCmd) < 0) return false;

        System.out.println(">>> ส่งข้อมูล...");
        if (aiwiSend(device, data) < 0) return false;

        System.out.println(">>> เขียนข้อมูลเสร็จสิ้น");
        return true;
    }

    /**
     * แสดงข้อมูลในรูปแบบ hex dump
     */
    static void printHex(String label, byte[] data) {
        System.out.printf("%s:%n", label);
        for (int i = 0; i < data.length; i++) {
            System.out.printf("  [%2d] = 0x%02X", i, data[i] & 0xFF);
            if ((i + 1) % 4 == 0) System.out.println();
        }
        System.out.println();
    }

    // ============================================================
    //  Main
    // ============================================================
    public static void main(String[] args) {
        System.out.println("==================================================");
        System.out.println("  AIWI Java Demo");
        System.out.println("==================================================");
        System.out.println();

        // เริ่มต้น hid4java services
        HidServicesSpecification spec = new HidServicesSpecification();
        spec.setAutoStart(false);
        HidServices hidServices = HidManager.getHidServices(spec);
        hidServices.start();

        // ค้นหาและเปิด device
        HidDevice device = hidServices.getHidDevice(AIWI_VID, AIWI_PID, null);
        if (device == null) {
            System.err.printf("[ERROR] ไม่พบ AIWI device (VID=0x%04X, PID=0x%04X)%n",
                              AIWI_VID, AIWI_PID);
            System.err.println("        ตรวจสอบว่าเสียบ AIWI แล้ว");
            hidServices.shutdown();
            System.exit(1);
        }

        try {
            // แสดงข้อมูล device
            System.out.println("Manufacturer : " + device.getManufacturer());
            System.out.println("Product      : " + device.getProduct());
            System.out.println("Serial Number: " + device.getSerialNumber());
            System.out.println();

            // ----------------------------------------------------------
            // ตัวอย่าง READ: อ่าน Block 05
            //
            // หมายเหตุสำคัญ:
            //   ค่า AES-encrypted ด้านล่างนี้เป็นเพียงตัวอย่างจาก C demo
            //   ในการใช้งานจริง ต้องสร้างโดย:
            //       1. นำ plaintext command เช่น "RDBL05" + padding รวม 16 bytes
            //       2. เข้ารหัสด้วย AES-256 ECB โดยใช้ Owner Key 32 bytes ที่ตั้งไว้ตอน setup
            //       3. ผลลัพธ์ 16 bytes + padding อีก 16 bytes รวม 32 bytes คือสิ่งที่ส่งไป
            //
            // ตัวอย่างการเข้ารหัสด้วย javax.crypto:
            //   import javax.crypto.Cipher;
            //   import javax.crypto.spec.SecretKeySpec;
            //
            //   byte[] key = "your_32_byte_owner_key_here.....".getBytes();  // 32 bytes
            //   byte[] plaintext = new byte[16];
            //   System.arraycopy("RDBL05".getBytes(), 0, plaintext, 0, 6);   // pad ด้วย 0x00
            //   SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            //   Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
            //   cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            //   byte[] encrypted16 = cipher.doFinal(plaintext);
            //   byte[] cmd = new byte[32];
            //   System.arraycopy(encrypted16, 0, cmd, 0, 16);               // 32 bytes รวม
            // ----------------------------------------------------------

            byte[] cmdRDBL05example = {
                0x60, (byte)0x8D, 0x68, (byte)0x80,
                (byte)0xE1, (byte)0xEC, 0x0E, (byte)0xC7,
                (byte)0xDB, (byte)0xCD, 0x78, (byte)0xDE,
                0x6F, 0x39, 0x67, 0x0F,
                (byte)0x81, (byte)0xC7, 0x7E, 0x67,
                0x5C, 0x7F, 0x52, (byte)0xED,
                0x48, (byte)0xC1, 0x19, (byte)0xE3,
                0x39, 0x07, (byte)0xB1, 0x58
            };

            byte[] response = readBlock(device, cmdRDBL05example);
            if (response != null) {
                printHex("ข้อมูลที่ได้รับจาก Block 05", response);
            }

            // ----------------------------------------------------------
            // ตัวอย่าง WRITE: เขียนข้อมูลลง Block 04
            //
            // ต้องสร้าง encryptedCmdWRBL04 ด้วยวิธีเดียวกับ RDBL
            // แต่ใช้ plaintext = "WRBL04" + padding
            //
            // ตัวอย่าง (pseudo code):
            //   byte[] encryptedCmdWRBL04 = aesEncrypt("WRBL04" + padding, ownerKey);
            //   byte[] dataToWrite = new byte[32];
            //   System.arraycopy("Hello AIWI Block04!!".getBytes(), 0, dataToWrite, 0, 20);
            //   writeBlock(device, encryptedCmdWRBL04, dataToWrite);
            // ----------------------------------------------------------
            System.out.println("(ตัวอย่าง WRITE — ดู comment ในโค้ดสำหรับรายละเอียด)");

        } finally {
            device.close();
            hidServices.shutdown();
            System.out.println("\nปิดการเชื่อมต่อแล้ว");
        }
    }
}
