/*---------------------------------------------------------*\
| GigabyteRGBFusion2USBController.cpp                       |
|                                                           |
|   Driver for Gigabyte Aorus RGB Fusion 2 USB motherboard  |
|                                                           |
|   jackun                                      08 Jan 2020 |
|   megadjc                                     31 Jul 2025 |
|                                                           |
|   This file is part of the OpenRGB project                |
|   SPDX-License-Identifier: GPL-2.0-or-later               |
\*---------------------------------------------------------*/

#include "GigabyteRGBFusion2USBController.h"

/*-------------------------------------------------------------------------*\
| Low level RGB value conversion table                                      |
| This is stored as a uint32_t in the chip so is trasmitted LSB to MSB      |
| Therefore the numbers represent the index where the controller will find  |
| respective colour in a regular packet                                     |
\*-------------------------------------------------------------------------*/
static RGBCalibration GigabyteCalibrationsLookup
{
    { "BGR", {{{0x00, 0x01, 0x02, 0x00}}}},
    { "BRG", {{{0x01, 0x00, 0x02, 0x00}}}},
    { "GRB", {{{0x02, 0x00, 0x01, 0x00}}}},
    { "GBR", {{{0x00, 0x02, 0x01, 0x00}}}},
    { "RGB", {{{0x02, 0x01, 0x00, 0x00}}}},
    { "RBG", {{{0x01, 0x02, 0x00, 0x00}}}}
};

/*---------------------------------------------------------*\
| Converts LED counts to divisions in hardware              |
\*---------------------------------------------------------*/
static LEDCount LedCountToEnum(unsigned int c)
{
    if(c <= 32)
    {
        return(LEDS_32);
    }
    else if(c <= 64)
    {
        return(LEDS_64);
    }
    else if(c <= 256)
    {
        return(LEDS_256);
    }
    else if(c <= 512)
    {
        return(LEDS_512);
    }
    else
    {
        return(LEDS_1024);
    }
}

RGBFusion2USBController::RGBFusion2USBController(hid_device* handle, const char* path, std::string mb_name, uint16_t pid): dev(handle), product_id(pid)
{
    name        = mb_name;
    location    = path;

    if(!RefreshHardwareInfo())
    {
        return;
    }
    if(report.support_cmd_flag >= 0x02)
    {
        EnableLampArray(false);
    }
    ResetController();
    EnableBeat(false);
}

RGBFusion2USBController::~RGBFusion2USBController()
{
    hid_close(dev);
}

/*---------------------------------------------------------*\
| Read configuration data from hardware.                    |
| Returns false if read fails.                              |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::RefreshHardwareInfo()
{
    unsigned char buffer[FUSION2_USB_BUFFER_SIZE] = {0};

    SendCCReport(0x60, 0x00);
    buffer[0] = report_id;
    int res = hid_get_feature_report(dev, buffer, sizeof(buffer));

    if(res < static_cast<int>(sizeof(IT8297Report)))
    {
        report_loaded = false;
        return false;
    }

    std::memcpy(&report, buffer, sizeof(IT8297Report));
    report_loaded = true;
    device_num  = report.device_num;
    description = std::string(report.str_product, 28);
    if(std::string::iterator nul = std::find(description.begin(), description.end(), '\0');
       nul != description.end())
    {
        description.erase(nul, description.end());
    }

    {
        char     text[16]{};

        std::snprintf(
            text,
            sizeof(text),
            "%u.%u.%u.%u",
            (report.fw_ver      ) & 0xFF,
            (report.fw_ver >>  8) & 0xFF,
            (report.fw_ver >> 16) & 0xFF,
            (report.fw_ver >> 24) & 0xFF
        );
        version = text;
        std::snprintf(text, sizeof(text), "0x%08X", report.chip_id);
        chip_id = text;
    }

    D_LED1_count = LedCountToEnum(report.curr_led_count_low  & 0x0F);
    D_LED2_count = LedCountToEnum((report.curr_led_count_low  >> 4) & 0x0F);
    D_LED3_count = LedCountToEnum(report.curr_led_count_high & 0x0F);
    D_LED4_count = LedCountToEnum((report.curr_led_count_high >> 4) & 0x0F);

    cal_data.dled[0]   = report.cal_strip0;
    cal_data.dled[1]   = report.cal_strip1;
    cal_data.mainboard = report.rgb_cali;
    cal_data.spare[0]  = report.cal_spare0;
    cal_data.spare[1]  = report.cal_spare1;

    cali_loaded = false;
    if(product_id == 0x5711)
    {
        unsigned char buffer2[FUSION2_USB_BUFFER_SIZE] = {0};
        SendCCReport(0x61, 0x00);
        buffer2[0] = report_id;
        int res2 = hid_get_feature_report(dev, buffer2, sizeof(buffer2));

        if(res2 >= static_cast<int>(sizeof(IT5711Calibration)))
        {
            IT5711Calibration   cali;

            std::memcpy(&cali, buffer2, sizeof(IT5711Calibration));
            cali_loaded = true;

            cal_data.dled[2]  = cali.cal_strip2;
            cal_data.dled[3]  = cali.cal_strip3;
            cal_data.spare[2] = cali.cal_spare2;
            cal_data.spare[3] = cali.cal_spare3;
        }
        else
        {
            cal_data.dled[2]  = 0;
            cal_data.dled[3]  = 0;
            cal_data.spare[2] = 0;
            cal_data.spare[3] = 0;
            cali_loaded       = false;
        }
    }
    else
    {
        cal_data.dled[2]  = 0;
        cal_data.dled[3]  = 0;
        cal_data.spare[2] = 0;
        cal_data.spare[3] = 0;
    }

    return report_loaded;
}

std::string RGBFusion2USBController::DecodeCalibrationBuffer(uint32_t value) const
{
    std::string out = "OFF";
    if(value == 0)
    {
        return out;
    }

    uint8_t bo_b =  value        & 0xFF;
    uint8_t bo_g = (value >> 8 ) & 0xFF;
    uint8_t bo_r = (value >> 16) & 0xFF;

    bool in_range = (bo_r < 3 && bo_g < 3 && bo_b < 3);
    bool distinct = (bo_r != bo_g && bo_r != bo_b && bo_g != bo_b);

    if(in_range && distinct)
    {
        out[bo_r] = 'R';
        out[bo_g] = 'G';
        out[bo_b] = 'B';
        return out;
    }

    return "BAD";
}

uint32_t RGBFusion2USBController::EncodeCalibrationBuffer(const std::string& rgb_order)
{
    if(rgb_order.empty())
    {
        return 0u;
    }

    std::string key = rgb_order;
    std::transform(key.begin(), key.end(), key.begin(),
                   [](unsigned char c){ return char(std::toupper(c)); });

    if(key=="OFF" || key=="0")
    {
        return 0u;
    }

    RGBCalibration::const_iterator it = GigabyteCalibrationsLookup.find(key);
    if(it == GigabyteCalibrationsLookup.end())
    {
        return 0u;
    }

    const RGBA &rgb_cal = it->second;
    return   (uint32_t(rgb_cal.raw[0]))
           | (uint32_t(rgb_cal.raw[1]) << 8)
           | (uint32_t(rgb_cal.raw[2]) << 16)
           | (uint32_t(rgb_cal.raw[3]) << 24);
}


EncodedCalibration RGBFusion2USBController::GetCalibration(bool refresh_from_hw)
{
    if(refresh_from_hw || !report_loaded || (product_id == 0x5711 && !cali_loaded))
    {
        if(!RefreshHardwareInfo())
        {
            return EncodedCalibration{};
        }
    }

    EncodedCalibration out{};
    out.dled[0]   = DecodeCalibrationBuffer(cal_data.dled[0]);
    out.dled[1]   = DecodeCalibrationBuffer(cal_data.dled[1]);
    out.spare[0]  = DecodeCalibrationBuffer(cal_data.spare[0]);
    out.spare[1]  = DecodeCalibrationBuffer(cal_data.spare[1]);
    out.mainboard = DecodeCalibrationBuffer(cal_data.mainboard);

    if(product_id == 0x5711)
    {
        out.dled[2]  = DecodeCalibrationBuffer(cal_data.dled[2]);
        out.dled[3]  = DecodeCalibrationBuffer(cal_data.dled[3]);
        out.spare[2] = DecodeCalibrationBuffer(cal_data.spare[2]);
        out.spare[3] = DecodeCalibrationBuffer(cal_data.spare[3]);
    }
    else
    {
        out.dled[2]  = "OFF";
        out.dled[3]  = "OFF";
        out.spare[2] = "OFF";
        out.spare[3] = "OFF";
    }

    return out;
}

bool RGBFusion2USBController::SetCalibration(const EncodedCalibration& cal, bool refresh_from_hw)
{
    if(refresh_from_hw && !RefreshHardwareInfo())
    {
        return false;
    }

    if(EncodeCalibrationBuffer(cal.dled[0])       == cal_data.dled[0]
        && EncodeCalibrationBuffer(cal.dled[1])   == cal_data.dled[1]
        && EncodeCalibrationBuffer(cal.mainboard) == cal_data.mainboard
        && EncodeCalibrationBuffer(cal.spare[0])  == cal_data.spare[0]
        && EncodeCalibrationBuffer(cal.spare[1])  == cal_data.spare[1]
        && (product_id != 0x5711
        || (EncodeCalibrationBuffer(cal.dled[2])  == cal_data.dled[2]
        && EncodeCalibrationBuffer(cal.dled[3])   == cal_data.dled[3]
        && EncodeCalibrationBuffer(cal.spare[2])  == cal_data.spare[2]
        && EncodeCalibrationBuffer(cal.spare[3])  == cal_data.spare[3])))
    {
        return true;
    }

    CMD_0x33 desired;

    desired.c.d_strip_c0        = EncodeCalibrationBuffer(cal.dled[0]);
    desired.c.d_strip_c1        = EncodeCalibrationBuffer(cal.dled[1]);
    desired.c.rgb_cali          = EncodeCalibrationBuffer(cal.mainboard);
    desired.c.c_spare0          = EncodeCalibrationBuffer(cal.spare[0]);
    desired.c.c_spare1          = EncodeCalibrationBuffer(cal.spare[1]);

    if(product_id == 0x5711)
    {
        desired.c.d_strip_c2    = EncodeCalibrationBuffer(cal.dled[2]);
        desired.c.d_strip_c3    = EncodeCalibrationBuffer(cal.dled[3]);
        desired.c.c_spare2      = EncodeCalibrationBuffer(cal.spare[2]);
        desired.c.c_spare3      = EncodeCalibrationBuffer(cal.spare[3]);
    }

    int rc = SendPacket(desired.buffer);
    if(rc < 0)
    {
        return false;
    }

    ResetController();
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    SaveCalState();
    std::this_thread::sleep_for(std::chrono::milliseconds(20));

    cal_data.dled[0]   = desired.c.d_strip_c0;
    cal_data.dled[1]   = desired.c.d_strip_c1;
    cal_data.mainboard = desired.c.rgb_cali;
    cal_data.spare[0]  = desired.c.c_spare0;
    cal_data.spare[1]  = desired.c.c_spare1;

    if(product_id == 0x5711)
    {
        cal_data.dled[2]  = desired.c.d_strip_c2;
        cal_data.dled[3]  = desired.c.d_strip_c3;
        cal_data.spare[2] = desired.c.c_spare2;
        cal_data.spare[3] = desired.c.c_spare3;
    }
    else
    {
        cal_data.dled[2]  = 0u;
        cal_data.dled[3]  = 0u;
        cal_data.spare[2] = 0u;
        cal_data.spare[3] = 0u;
    }

    return true;
}

void RGBFusion2USBController::SetLedCount(unsigned int c0, unsigned int c1, unsigned int c2, unsigned int c3)
{
    LEDCount new_d1 = LedCountToEnum(c0);
    LEDCount new_d2 = LedCountToEnum(c1);
    LEDCount new_d3 = LedCountToEnum(c2);
    LEDCount new_d4 = LedCountToEnum(c3);

    if(new_d1 == D_LED1_count && new_d2 == D_LED2_count && new_d3 == D_LED3_count && new_d4 == D_LED4_count)
    {
        return;
    }

    D_LED1_count    = new_d1;
    D_LED2_count    = new_d2;
    D_LED3_count    = new_d3;
    D_LED4_count    = new_d4;

    SendCCReport(0x34, (new_d2 << 4) | new_d1, (new_d4 << 4) | new_d3);
}

/*---------------------------------------------------------*\
| Switch ARGB header mode (single/addressable)              |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
{
    int bitmask = 0;

    if(hdr == -1)
    {
        bitmask = 0x01 | 0x02 | 0x08 | 0x10;
    }
    else
    {
        switch(hdr)
        {
            case LED4:
            case HDR_D_LED2:
            case HDR_D_LED2_ARGB:
                bitmask = 0x02;
                break;
            case HDR_D_LED3:
            case HDR_D_LED3_ARGB:
                bitmask = 0x08;
                break;
            case HDR_D_LED4:
            case HDR_D_LED4_ARGB:
                bitmask = 0x10;
                break;
            default:
                bitmask = 0x01;
                break;
        }
    }

        int base_mask = (effect_disabled < 0) ? 0 : effect_disabled;
        int new_effect_disabled = enable
            ? (base_mask & ~bitmask)
            : (base_mask | bitmask);

        // Skip redundant writes only after we have synchronized at least once
        if(effect_disabled >= 0 && new_effect_disabled == effect_disabled)
    {
        return true;
    }
    effect_disabled = new_effect_disabled;
    int res = SendCCReport(0x32, effect_disabled);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    return res;
}

/*---------------------------------------------------------*\
| Persist LED config data                                   |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SaveLEDState(bool e)
{
    return SendCCReport(0x47, e ? 1 : 0);
}

/*---------------------------------------------------------*\
| Persist calibration                                       |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SaveCalState()
{
    return SendCCReport(0x5E, 0);
}

/*---------------------------------------------------------*\
| Set beat mode (hardware audio sync mode)                  |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::EnableBeat(bool e)
{
    return SendCCReport(0x31, e ? 1 : 0);
}

/*---------------------------------------------------------*\
| Set Lamp Array mode (MSDL)                                |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::EnableLampArray(bool enable)
{
    return SendCCReport(0x48, enable ? 1 : 0);
}

std::string RGBFusion2USBController::GetDeviceName()
{
    return(name);
}

std::string RGBFusion2USBController::GetDeviceDescription()
{
    return(description);
}

std::string RGBFusion2USBController::GetFWVersion()
{
    return(version);
}

std::string RGBFusion2USBController::GetDeviceLocation()
{
    return("HID: " + location);
}

std::string RGBFusion2USBController::GetSerial()
{
    return(chip_id);
}

/*---------------------------------------------------------*\
| PID (controller feature support)                          |
\*---------------------------------------------------------*/
uint16_t RGBFusion2USBController::GetProductID()
{
    return(product_id);
}

/*---------------------------------------------------------*\
| Low level controller number (multi-controller)            |
\*---------------------------------------------------------*/
uint8_t  RGBFusion2USBController::GetDeviceNum()
{
    return(device_num);
}

/*---------------------------------------------------------*\
| Set ARGB strips (addressable)                             |
\*---------------------------------------------------------*/
void RGBFusion2USBController::SetStripColors(unsigned int hdr, RGBColor* colors, unsigned int num_colors, int single_led)
{
    PktRGB pkt;
    pkt.Init(hdr, report_id);
    uint32_t byteorder;

    switch(pkt.s.header)
    {
        case HDR_D_LED2_ARGB:
            byteorder = cal_data.dled[1];
            break;
        case HDR_D_LED3_ARGB:
            byteorder = cal_data.dled[2];
            break;
        case HDR_D_LED4_ARGB:
            byteorder = cal_data.dled[3];
            break;
        default:
            byteorder = cal_data.dled[0];
            break;
    }

    unsigned char bo_r  = byteorder >> 16;
    unsigned char bo_g  = byteorder >> 8;
    unsigned char bo_b  = byteorder & 0xFF;

    int res;
    int leds_left       = num_colors;
    int sent_data       = 0;
    int k               = 0;
    int leds_in_pkt     = sizeof(pkt.s.leds) / sizeof(*pkt.s.leds); /* 19 */

    if(single_led > -1)
    {
        leds_left       = 1;
        k               = single_led;
        sent_data       = k * 3;
        leds_in_pkt     = 1;
    }

    while(leds_left > 0)
    {
        leds_in_pkt     = (std::min)(leds_in_pkt, leds_left);
        leds_left      -= leds_in_pkt;

        pkt.s.bcount    = leds_in_pkt * 3;
        pkt.s.boffset   = sent_data;
        sent_data      += pkt.s.bcount;

        for(int i = 0; i < leds_in_pkt; i++)
        {
            RGBColor            color = colors[k];
            uint8_t            offset = (i * 3) + 5;

            pkt.buffer[offset + bo_r] = RGBGetRValue(color);
            pkt.buffer[offset + bo_g] = RGBGetGValue(color);
            pkt.buffer[offset + bo_b] = RGBGetBValue(color);
            k++;
        }

        res = SendPacket(pkt.buffer);

        if(res < 0)
        {
            return;
        }
    }
}

/*---------------------------------------------------------*\
| Set hardware effects (single)                             |
| Note: Effects paramters match that of gigabyte software.  |
| -(2)Gigabyte breathe ranges are 400-1000ms in 100ms steps |
|     and 1000-1600ms in 200ms steps                        |
| -(3)Gigabyte flash ranges are 600-2400ms in 200ms steps   |
| -(4)Gigabyte color cycle ranges are 300-2400ms for period0|
|     and 100-2200ms for period1 in 100ms steps.            |
|     the follow this trend between 300-1100/100-1000ms     |
|     then jump to 2400ms and 2200ms respective on speed 9. |
| -(6)Gigabyte Wave ranges are 30-300ms in steps following  |
|     the following formula. 2.5(s+1)^2 + 2.5(s+1) + 25.    |
| -(15)Gigabyte dflash ranges are 800-2600ms in 200ms steps |
| -(3)(15)flash and dflash parameters were combined.        |
\*---------------------------------------------------------*/
void RGBFusion2USBController::SetLEDEffect(int led, int mode, unsigned int speed, unsigned char brightness, bool random, uint32_t* color)
{
    PktEffect pkt;
    pkt.Init(led, report_id, product_id);
    if(led == -1)
    {
        effect_zone_mask            = pkt.e.zone0;
    }
    else if((effect_zone_mask & pkt.e.zone0) == 0)
    {
        effect_zone_mask           |= pkt.e.zone0;
    }
    pkt.e.max_brightness            = brightness;
    pkt.e.effect_type               = mode;
    pkt.e.effect_param0             = random ? 7 : 0;
    pkt.e.color0                    = RGBToBGRColor(*color);

    switch(mode)
    {
        case EFFECT_PULSE:
            pkt.e.period0           = (speed <= 6) ? (400 + speed * 100) : (1000 + (speed - 6) * 200);
            pkt.e.period1           = pkt.e.period0;
            pkt.e.period2           = 200;
            break;
        case EFFECT_DFLASH:
            pkt.e.effect_type       = 3;
            pkt.e.effect_param1     = 1;
            pkt.e.effect_param2     = 2;
        case EFFECT_BLINKING:
            pkt.e.period0           = 100;
            pkt.e.period1           = 100;
            pkt.e.period2           = (speed * 200) + 700;
            break;
        case EFFECT_COLORCYCLE:
            pkt.e.period0           = (speed * 100 + 300) + (speed > 8 ? 1300 * (speed - 8) : 0);
            pkt.e.period1           = pkt.e.period0 -200;
            pkt.e.effect_param0     = 7;
            break;
        case EFFECT_WAVE:
            pkt.e.period0           = (((speed + 1)^2) + (speed + 1) + 10) * 5 / 2;
            pkt.e.effect_param0     = 7;
            pkt.e.effect_param1     = 1;
            break;
        case EFFECT_RANDOM:
            pkt.e.period0           = 100;
            pkt.e.effect_param0     = 1;
            pkt.e.effect_param1     = 5;
            break;
        case EFFECT_WAVE1:
            pkt.e.period0           = 1200;
            pkt.e.period1           = 100;
            pkt.e.period2           = 360;
            pkt.e.period3           = 1200;
            break;
        case EFFECT_WAVE2:
        case EFFECT_WAVE4:
            pkt.e.period0           = 200;
            pkt.e.effect_param0     = 7;
            break;
        case EFFECT_WAVE3:
            pkt.e.period0           = 840;
            pkt.e.period1           = 20;
            pkt.e.period2           = 200;
            pkt.e.period3           = 840;
            break;
    }
    SendPacket(pkt.buffer);
}

/*---------------------------------------------------------*\
| Apply hardware effects (single)                           |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::ApplyEffect(bool fast_apply)
{
    if(fast_apply)
    {
        if(product_id == 0x5711)
        {
            return SendCCReport(0x28, 0xFF, 0x07);
        }
        else
        {
            return SendCCReport(0x28, 0xFF, 0x00);
        }
    }

    PktEffectApply pkt = {};
    pkt.a.zone_sel0 = effect_zone_mask;

    effect_zone_mask = 0;
    return SendPacket(pkt.buffer);
}

bool RGBFusion2USBController::SendCCReport(uint8_t a, uint8_t b, uint8_t c)
{
    return(SendReport(report_id, a, b, c));
}

bool RGBFusion2USBController::SendReport(uint8_t id, uint8_t a, uint8_t b, uint8_t c)
{
    unsigned char buffer[FUSION2_USB_BUFFER_SIZE] {};
    std::memset(buffer, 0, FUSION2_USB_BUFFER_SIZE);

    buffer[0] = id;
    buffer[1] = a;
    buffer[2] = b;
    buffer[3] = c;

    return(SendPacket(buffer) == FUSION2_USB_BUFFER_SIZE);
}

int RGBFusion2USBController::SendPacket(unsigned char* packet)
{
    return hid_send_feature_report(dev, packet, FUSION2_USB_BUFFER_SIZE);
}

/*---------------------------------------------------------*\
| Reset controller parameters                               |
\*---------------------------------------------------------*/
void RGBFusion2USBController::ResetController()
{
    for(uint8_t reg = 0x20; reg <= 0x27; ++reg)
    {
        SendCCReport(reg, 0x00, 0x00);
    }

    if(product_id == 0x5711)
    {
        for(uint8_t reg = 0x90; reg <= 0x92; ++reg)
        {
            SendCCReport(reg, 0x00, 0x00);
        }
    }
    ApplyEffect(true);
}

/*---------------------------------------------------------*\
| Check controller for gen2 ARGB support                    |
|   Checks for supported device number                      |
|   Then checks for supported controllers                   |
|   Then checks for supported feature bit (SaveLEDState)    |
|   Finally checks for strip detection value.               |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SupportsGen2() const
{
    bool supports_gen2 = false;

    supports_gen2 =  (device_num == 0x00)
                  && (product_id == 0x5702 || product_id==0x5711 || product_id==0x8950)
                  && (report.support_cmd_flag & 0x01)
                  && (report.strip_detect == 0x01);

    return supports_gen2;
}

std::vector<Gen2StripInfo> RGBFusion2USBController::ExportGen2Strips() const
{
    size_t count = (product_id == 0x5711) ? 4u : 2u;
    std::vector<Gen2StripInfo> out;
    out.reserve(count);
    for(size_t i = 0; i < count; ++i)
    {
        out.push_back(g2_strip_info[i]);
    }
    return out;
}

/*---------------------------------------------------------*\
| Scan Headers for Gen2 Devices                             |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::ScanGen2Strips()
{
    for(unsigned i = 0; i < 4; ++i)
    {
        if(g2_strip_info[i].LedsOfStrip.capacity() < 15)
        {
            g2_strip_info[i].LedsOfStrip.reserve(15);
        }
        g2_strip_info[i].numStrip  = 0;
        g2_strip_info[i].totalLeds = 0;
        g2_strip_info[i].LedsOfStrip.resize(0);
    }

    const unsigned int hdr_lim = (product_id == 0x5711) ? 4u : 2u;

    for(unsigned int slot = 0; slot < hdr_lim; ++slot)
    {
        static constexpr uint8_t delta[4] = {4, 5, 0, 1};
        uint8_t scan_cmd = static_cast<uint8_t>(GEN2_LED_BASE_SCAN + delta[slot]);
        uint8_t info_cmd = static_cast<uint8_t>(scan_cmd + 2);

        if(!SendCCReport(scan_cmd, 0x00, 0x00))
        {
            return false;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(700));

        if(!SendCCReport(info_cmd, 0x00, 0x00))
        {
            return false;
        }

        unsigned char feature_buf[64] = {0};
        feature_buf[0] = report_id;
        int recv_len = hid_get_feature_report(dev, feature_buf, sizeof(feature_buf));
        if(recv_len < 64)
        {
            return false;
        }

        int seg_count = static_cast<int>(feature_buf[1]);
        if(seg_count < 0)
        {
            seg_count = 0;
        }
        if(seg_count > 15)
        {
            seg_count = 15;
        }
        Gen2StripInfo& dst = g2_strip_info[slot];
        dst.numStrip = static_cast<uint8_t>(seg_count);
        dst.LedsOfStrip.resize(static_cast<size_t>(seg_count));

        uint32_t total_leds = 0;
        const int counts_base = 2;

        for(int k = 0; k < seg_count; ++k)
        {
            const int off = counts_base + (k * 2);
            uint16_t lo   = static_cast<uint16_t>(feature_buf[off + 0]);
            uint16_t hi   = static_cast<uint16_t>(feature_buf[off + 1]);
            uint16_t cnt  = static_cast<uint16_t>(lo | (hi << 8));

            dst.LedsOfStrip[static_cast<size_t>(k)] = cnt;
            total_leds += cnt;
        }

        dst.totalLeds = total_leds;

        SetLedCount(0, 0, 0, 0);
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
        SaveLEDState(false);
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }

    return true;
}
