/*---------------------------------------------------------*\
|  RGBController_GigabyteCastor3.cpp                        |
|                                                           |
|  RGBController for Gigabyte Aorus Waterforce X II 360     |
|  AIO Cooler (Castor3 controller)                          |
|                                                           |
|  This file is part of the OpenRGB project                 |
|  SPDX-License-Identifier: GPL-2.0-or-later               |
\*---------------------------------------------------------*/

#include "RGBController_GigabyteCastor3.h"

/**------------------------------------------------------------------*\
    @name Gigabyte Aorus Waterforce X II 360
    @category Cooler
    @type USB
    @save :o:
    @direct :o:
    @effects :white_check_mark:
    @detectors DetectGigabyteCastor3Controllers
    @comment Controls the LED ring on the pump head of the
        Gigabyte Aorus Waterforce X II 360 AIO cooler.
        Uses the Castor3 USB HID controller (VID 0x0414, PID 0x7A5E).
        The LED ring and fans share the same controller — they are
        not independently addressable and follow the same effect.
        LCD display control is not implemented.
        No direct/per-LED mode available — all modes are hardware
        effects with a single color or palette.
\*-------------------------------------------------------------------*/

RGBController_GigabyteCastor3::RGBController_GigabyteCastor3(GigabyteCastor3Controller* controller_ptr)
{
    controller  = controller_ptr;

    name        = "Gigabyte Aorus Waterforce X II 360";
    vendor      = "Gigabyte";
    type        = DEVICE_TYPE_COOLER;
    description = "Gigabyte Aorus Waterforce X II 360 AIO Cooler";
    location    = controller->GetDeviceLocation();
    serial      = controller->GetSerialString();
    version     = controller->GetFirmwareVersion();

    /*---------------------------------------------------------*\
    |  All modes from castor3.py LED_EFFECTS (12 effects + off) |
    |                                                           |
    |  The Castor3 has no direct/per-LED mode. All effects are  |
    |  hardware-driven, either single-color, palette, or        |
    |  firmware-color (no user color).                          |
    |                                                           |
    |  Speed: 1-5 mapped to MODE_FLAG_HAS_SPEED                |
    |  Brightness: 1-10 mapped to MODE_FLAG_HAS_BRIGHTNESS      |
    |                                                           |
    |  mode.value stores the style byte for the c9 command.     |
    |  mode.speed stores 1-5 (scaled to speed*20 on wire).      |
    |  mode.brightness stores 1-10 (direct on wire).            |
    \*---------------------------------------------------------*/

    /*---------------------------------------------------------*\
    |  Off                                                      |
    |  Wire: Style=0x01 (Static) with speed_wire=0              |
    \*---------------------------------------------------------*/
    mode Off;
    Off.name            = "Off";
    Off.value           = 0xFF;     /* sentinel — handled specially */
    Off.flags           = 0;
    Off.color_mode      = MODE_COLORS_NONE;
    modes.push_back(Off);

    /*---------------------------------------------------------*\
    |  Static — single color, no speed                          |
    \*---------------------------------------------------------*/
    mode Static;
    Static.name         = "Static";
    Static.value        = CASTOR3_STYLE_STATIC;
    Static.flags        = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    Static.colors_min   = 1;
    Static.colors_max   = 1;
    Static.color_mode   = MODE_COLORS_MODE_SPECIFIC;
    Static.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Static.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Static.brightness   = CASTOR3_BRIGHTNESS_DEFAULT;
    Static.colors.resize(1);
    modes.push_back(Static);

    /*---------------------------------------------------------*\
    |  Pulse — single color, speed + brightness                 |
    \*---------------------------------------------------------*/
    mode Pulse;
    Pulse.name          = "Pulse";
    Pulse.value         = CASTOR3_STYLE_PULSE;
    Pulse.flags         = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    Pulse.speed_min     = CASTOR3_SPEED_MIN;
    Pulse.speed_max     = CASTOR3_SPEED_MAX;
    Pulse.speed         = CASTOR3_SPEED_DEFAULT;
    Pulse.colors_min    = 1;
    Pulse.colors_max    = 1;
    Pulse.color_mode    = MODE_COLORS_MODE_SPECIFIC;
    Pulse.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Pulse.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Pulse.brightness    = CASTOR3_BRIGHTNESS_DEFAULT;
    Pulse.colors.resize(1);
    modes.push_back(Pulse);

    /*---------------------------------------------------------*\
    |  Flash — single color, speed + brightness                 |
    \*---------------------------------------------------------*/
    mode Flash;
    Flash.name          = "Flash";
    Flash.value         = CASTOR3_STYLE_FLASH;
    Flash.flags         = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    Flash.speed_min     = CASTOR3_SPEED_MIN;
    Flash.speed_max     = CASTOR3_SPEED_MAX;
    Flash.speed         = CASTOR3_SPEED_DEFAULT;
    Flash.colors_min    = 1;
    Flash.colors_max    = 1;
    Flash.color_mode    = MODE_COLORS_MODE_SPECIFIC;
    Flash.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Flash.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Flash.brightness    = CASTOR3_BRIGHTNESS_DEFAULT;
    Flash.colors.resize(1);
    modes.push_back(Flash);

    /*---------------------------------------------------------*\
    |  Double Flash — single color, speed + brightness          |
    \*---------------------------------------------------------*/
    mode DFlash;
    DFlash.name         = "Double Flash";
    DFlash.value        = CASTOR3_STYLE_DFLASH;
    DFlash.flags        = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    DFlash.speed_min    = CASTOR3_SPEED_MIN;
    DFlash.speed_max    = CASTOR3_SPEED_MAX;
    DFlash.speed        = CASTOR3_SPEED_DEFAULT;
    DFlash.colors_min   = 1;
    DFlash.colors_max   = 1;
    DFlash.color_mode   = MODE_COLORS_MODE_SPECIFIC;
    DFlash.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    DFlash.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    DFlash.brightness   = CASTOR3_BRIGHTNESS_DEFAULT;
    DFlash.colors.resize(1);
    modes.push_back(DFlash);

    /*---------------------------------------------------------*\
    |  Cycle — firmware colors, speed only                      |
    \*---------------------------------------------------------*/
    mode Cycle;
    Cycle.name          = "Spectrum Cycle";
    Cycle.value         = CASTOR3_STYLE_CYCLE;
    Cycle.flags         = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
    Cycle.speed_min     = CASTOR3_SPEED_MIN;
    Cycle.speed_max     = CASTOR3_SPEED_MAX;
    Cycle.speed         = CASTOR3_SPEED_DEFAULT;
    Cycle.color_mode    = MODE_COLORS_NONE;
    Cycle.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Cycle.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Cycle.brightness    = CASTOR3_BRIGHTNESS_DEFAULT;
    modes.push_back(Cycle);

    /*---------------------------------------------------------*\
    |  Gradient — single color, speed + brightness              |
    |  Note: b4 = brightness on wire (special case)             |
    \*---------------------------------------------------------*/
    mode Gradient;
    Gradient.name       = "Gradient";
    Gradient.value      = CASTOR3_STYLE_GRADIENT;
    Gradient.flags      = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    Gradient.speed_min  = CASTOR3_SPEED_MIN;
    Gradient.speed_max  = CASTOR3_SPEED_MAX;
    Gradient.speed      = CASTOR3_SPEED_DEFAULT;
    Gradient.colors_min = 1;
    Gradient.colors_max = 1;
    Gradient.color_mode = MODE_COLORS_MODE_SPECIFIC;
    Gradient.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Gradient.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Gradient.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
    Gradient.colors.resize(1);
    modes.push_back(Gradient);

    /*---------------------------------------------------------*\
    |  Color Shift — 8-color palette, speed + brightness        |
    \*---------------------------------------------------------*/
    mode ColorShift;
    ColorShift.name         = "Color Shift";
    ColorShift.value        = CASTOR3_STYLE_COLORSHIFT;
    ColorShift.flags        = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    ColorShift.speed_min    = CASTOR3_SPEED_MIN;
    ColorShift.speed_max    = CASTOR3_SPEED_MAX;
    ColorShift.speed        = CASTOR3_SPEED_DEFAULT;
    ColorShift.colors_min   = 1;
    ColorShift.colors_max   = 8;
    ColorShift.color_mode   = MODE_COLORS_MODE_SPECIFIC;
    ColorShift.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    ColorShift.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    ColorShift.brightness   = CASTOR3_BRIGHTNESS_DEFAULT;
    ColorShift.colors.resize(8);
    ColorShift.colors[0] = ToRGBColor(0xFF, 0x00, 0x00);
    ColorShift.colors[1] = ToRGBColor(0xFF, 0x72, 0x00);
    ColorShift.colors[2] = ToRGBColor(0xFF, 0xFF, 0x00);
    ColorShift.colors[3] = ToRGBColor(0x00, 0xFF, 0x00);
    ColorShift.colors[4] = ToRGBColor(0x00, 0xFF, 0xFF);
    ColorShift.colors[5] = ToRGBColor(0x00, 0x00, 0xFF);
    ColorShift.colors[6] = ToRGBColor(0xFF, 0x00, 0xFF);
    ColorShift.colors[7] = ToRGBColor(0xFF, 0x80, 0x80);
    modes.push_back(ColorShift);

    /*---------------------------------------------------------*\
    |  Wave — firmware colors, speed only                       |
    \*---------------------------------------------------------*/
    mode Wave;
    Wave.name           = "Wave";
    Wave.value          = CASTOR3_STYLE_WAVE;
    Wave.flags          = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
    Wave.speed_min      = CASTOR3_SPEED_MIN;
    Wave.speed_max      = CASTOR3_SPEED_MAX;
    Wave.speed          = CASTOR3_SPEED_DEFAULT;
    Wave.color_mode     = MODE_COLORS_NONE;
    Wave.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Wave.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Wave.brightness     = CASTOR3_BRIGHTNESS_DEFAULT;
    modes.push_back(Wave);

    /*---------------------------------------------------------*\
    |  Rainbow — firmware colors, speed only                    |
    \*---------------------------------------------------------*/
    mode Rainbow;
    Rainbow.name        = "Rainbow";
    Rainbow.value       = CASTOR3_STYLE_RAINBOW;
    Rainbow.flags       = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
    Rainbow.speed_min   = CASTOR3_SPEED_MIN;
    Rainbow.speed_max   = CASTOR3_SPEED_MAX;
    Rainbow.speed       = CASTOR3_SPEED_DEFAULT;
    Rainbow.color_mode  = MODE_COLORS_NONE;
    Rainbow.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Rainbow.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Rainbow.brightness  = CASTOR3_BRIGHTNESS_DEFAULT;
    modes.push_back(Rainbow);

    /*---------------------------------------------------------*\
    |  Tri-Color — 3-color palette, speed + brightness          |
    \*---------------------------------------------------------*/
    mode TriColor;
    TriColor.name       = "Tri-Color";
    TriColor.value      = CASTOR3_STYLE_TRICOLOR;
    TriColor.flags      = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    TriColor.speed_min  = CASTOR3_SPEED_MIN;
    TriColor.speed_max  = CASTOR3_SPEED_MAX;
    TriColor.speed      = CASTOR3_SPEED_DEFAULT;
    TriColor.colors_min = 1;
    TriColor.colors_max = 3;
    TriColor.color_mode = MODE_COLORS_MODE_SPECIFIC;
    TriColor.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    TriColor.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    TriColor.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
    TriColor.colors.resize(3);
    TriColor.colors[0]  = ToRGBColor(0x00, 0x00, 0xFF);
    TriColor.colors[1]  = ToRGBColor(0x7D, 0x00, 0xFF);
    TriColor.colors[2]  = ToRGBColor(0xFF, 0x00, 0xFF);
    modes.push_back(TriColor);

    /*---------------------------------------------------------*\
    |  Spin — 3-color palette, speed + brightness               |
    \*---------------------------------------------------------*/
    mode Spin;
    Spin.name           = "Spin";
    Spin.value          = CASTOR3_STYLE_SPIN;
    Spin.flags          = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    Spin.speed_min      = CASTOR3_SPEED_MIN;
    Spin.speed_max      = CASTOR3_SPEED_MAX;
    Spin.speed          = CASTOR3_SPEED_DEFAULT;
    Spin.colors_min     = 1;
    Spin.colors_max     = 3;
    Spin.color_mode     = MODE_COLORS_MODE_SPECIFIC;
    Spin.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Spin.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Spin.brightness     = CASTOR3_BRIGHTNESS_DEFAULT;
    Spin.colors.resize(3);
    Spin.colors[0]      = ToRGBColor(0xFF, 0x00, 0xFE);
    Spin.colors[1]      = ToRGBColor(0x00, 0xFF, 0xFB);
    Spin.colors[2]      = ToRGBColor(0xFF, 0xFF, 0x00);
    modes.push_back(Spin);

    /*---------------------------------------------------------*\
    |  Switch — 2-color palette, speed + brightness             |
    \*---------------------------------------------------------*/
    mode Switch;
    Switch.name         = "Switch";
    Switch.value        = CASTOR3_STYLE_SWITCH;
    Switch.flags        = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
    Switch.speed_min    = CASTOR3_SPEED_MIN;
    Switch.speed_max    = CASTOR3_SPEED_MAX;
    Switch.speed        = CASTOR3_SPEED_DEFAULT;
    Switch.colors_min   = 1;
    Switch.colors_max   = 2;
    Switch.color_mode   = MODE_COLORS_MODE_SPECIFIC;
    Switch.brightness_min = CASTOR3_BRIGHTNESS_MIN;
    Switch.brightness_max = CASTOR3_BRIGHTNESS_MAX;
    Switch.brightness   = CASTOR3_BRIGHTNESS_DEFAULT;
    Switch.colors.resize(2);
    Switch.colors[0]    = ToRGBColor(0xFF, 0x00, 0xFE);
    Switch.colors[1]    = ToRGBColor(0x00, 0xFF, 0xFB);
    modes.push_back(Switch);

    SetupZones();
}

RGBController_GigabyteCastor3::~RGBController_GigabyteCastor3()
{
    delete controller;
}

void RGBController_GigabyteCastor3::SetupZones()
{
    /*---------------------------------------------------------*\
    |  Single zone: LED ring + fans (not independently          |
    |  addressable — fans follow the pump ring effect)          |
    |                                                           |
    |  This is a SINGLE zone because the protocol does not      |
    |  expose per-LED addressing. All LEDs display the same     |
    |  hardware effect simultaneously.                          |
    \*---------------------------------------------------------*/
    zone led_ring;
    led_ring.name       = "LED Ring";
    led_ring.type       = ZONE_TYPE_SINGLE;
    led_ring.leds_min   = 1;
    led_ring.leds_max   = 1;
    led_ring.leds_count = 1;
    led_ring.matrix_map = NULL;
    zones.push_back(led_ring);

    led new_led;
    new_led.name = "LED Ring";
    leds.push_back(new_led);

    SetupColors();
}

void RGBController_GigabyteCastor3::ResizeZone(int /*zone*/, int /*new_size*/)
{
    /*---------------------------------------------------------*\
    |  Fixed zone size — not resizable                          |
    \*---------------------------------------------------------*/
}

void RGBController_GigabyteCastor3::DeviceUpdateLEDs()
{
    DeviceUpdateMode();
}

void RGBController_GigabyteCastor3::UpdateZoneLEDs(int /*zone*/)
{
    DeviceUpdateLEDs();
}

void RGBController_GigabyteCastor3::UpdateSingleLED(int /*led*/)
{
    DeviceUpdateLEDs();
}

void RGBController_GigabyteCastor3::DeviceUpdateMode()
{
    unsigned char style = (unsigned char)modes[active_mode].value;

    /*---------------------------------------------------------*\
    |  Handle Off mode (sentinel value 0xFF)                    |
    \*---------------------------------------------------------*/
    if(style == 0xFF)
    {
        controller->SetOff();
        return;
    }

    /*---------------------------------------------------------*\
    |  Determine speed, brightness, b4, b5, and color type      |
    |  from the effect lookup table matching castor3.py          |
    \*---------------------------------------------------------*/
    unsigned char speed      = CASTOR3_SPEED_DEFAULT;
    unsigned char brightness = CASTOR3_BRIGHTNESS_DEFAULT;

    if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED)
    {
        speed = (unsigned char)modes[active_mode].speed;
    }

    if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS)
    {
        brightness = (unsigned char)modes[active_mode].brightness;
    }

    /*---------------------------------------------------------*\
    |  Look up b4, b5, and color_type per effect                |
    |  These match the LED_EFFECTS dict in castor3.py           |
    \*---------------------------------------------------------*/
    unsigned char b4 = 0x02;
    unsigned char b5 = 0x01;
    castor3_color_type color_type = CASTOR3_COLORS_NONE;

    switch(style)
    {
        case CASTOR3_STYLE_STATIC:
        case CASTOR3_STYLE_PULSE:
        case CASTOR3_STYLE_FLASH:
        case CASTOR3_STYLE_DFLASH:
            b4 = 0x02;
            b5 = 0x01;
            color_type = CASTOR3_COLORS_SINGLE;
            break;

        case CASTOR3_STYLE_GRADIENT:
            /*-------------------------------------------------*\
            |  Gradient: b4 = brightness on wire (special case)  |
            \*-------------------------------------------------*/
            b4 = brightness;
            b5 = 0x01;
            color_type = CASTOR3_COLORS_SINGLE;
            break;

        case CASTOR3_STYLE_CYCLE:
        case CASTOR3_STYLE_WAVE:
        case CASTOR3_STYLE_RAINBOW:
            b4 = 0x02;
            b5 = 0x01;
            color_type = CASTOR3_COLORS_NONE;
            break;

        case CASTOR3_STYLE_COLORSHIFT:
            b4 = 0x08;     /* ClrCount=8 */
            b5 = 0x02;     /* CmbIndex+1=2 */
            color_type = CASTOR3_COLORS_PALETTE;
            break;

        case CASTOR3_STYLE_TRICOLOR:
        case CASTOR3_STYLE_SPIN:
            b4 = 0x02;
            b5 = 0x01;
            color_type = CASTOR3_COLORS_PALETTE;
            break;

        case CASTOR3_STYLE_SWITCH:
            b4 = 0x02;
            b5 = 0x01;
            color_type = CASTOR3_COLORS_PALETTE;
            break;
    }

    controller->SetEffect(style, speed, brightness, b4, b5,
                          color_type, modes[active_mode].colors);
}
