/*
* Example for using I2C-MP-USB in C/C++
*
* Author: Alexander Jasper
*
* This source code is based on the Python library and was created with
* support from ChatGPT. It can be used freely without any warranty.
*
* I2C-MP-USB: https://www.fischl.de/i2c-mp-usb/
* Python-Lib: https://github.com/EmbedME/pyI2C_MP_USB/
*/

#define CMD_ECHO 0
#define CMD_GET_FUNC 1
#define CMD_SET_DELAY 2
#define CMD_GET_STATUS 3
#define CMD_I2C_IO 4
#define CMD_I2C_IO_BEGIN 1
#define CMD_I2C_IO_END 2
#define CMD_START_BOOTLOADER 0x10
#define CMD_SET_BAUDRATE 0x11
#define I2C_M_RD 0x01
#define I2C_MP_USB_VID 0x0403
#define I2C_MP_USB_PID 0xc631

libusb_device_handle* usbhandle;

void initialize_usb_device() {
    libusb_context* ctx;
    libusb_init(&ctx);

    libusb_device** list;
    ssize_t cnt = libusb_get_device_list(ctx, &list);
    if (cnt < 0) {
        fprintf(stderr, "Error getting device list\n");
        exit(1);
    }

    int found = 0;
    for (ssize_t i = 0; i < cnt; i++) {
        struct libusb_device_descriptor desc;
        libusb_get_device_descriptor(list[i], &desc);
        if (desc.idVendor == I2C_MP_USB_VID && desc.idProduct == I2C_MP_USB_PID) {
            libusb_open(list[i], &usbhandle);
            found = 1;
            break;
        }
    }

    libusb_free_device_list(list, 1);

    if (!found) {
        fprintf(stderr, "I2C_MP_USB device not found\n");
        exit(1);
    }

    libusb_claim_interface(usbhandle, 0);
}

void close_usb_device() {
    libusb_close(usbhandle);
    libusb_exit(NULL);
}

void perform_control_transfer(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, uint8_t* data, uint16_t length) {
    int result = libusb_control_transfer(usbhandle, request_type, request, value, index, data, length, 0);
    if (result < 0) {
        fprintf(stderr, "Control transfer failed: %s\n", libusb_strerror(result));
    }
}

void read_i2c_block_data(int i2c_addr, int register_addr, int length, uint8_t* data) {
    std::vector<uint8_t> buf(1 + length);
    buf[0] = register_addr;


    perform_control_transfer((LIBUSB_REQUEST_TYPE_CLASS & ~LIBUSB_ENDPOINT_DIR_MASK) |  LIBUSB_ENDPOINT_OUT,
        CMD_I2C_IO + CMD_I2C_IO_BEGIN, 0, i2c_addr, &buf[0], 1);

    perform_control_transfer((LIBUSB_REQUEST_TYPE_CLASS & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_IN,
        CMD_I2C_IO + CMD_I2C_IO_END, I2C_M_RD, i2c_addr, &buf[1], length);


    // Copy the received data to the output buffer
    memcpy(data, &buf[1], length);
}
