/**
 * @file rgb_ring.c
 * @author astro
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "rgb_ring.h"

#include <string.h>
#include "quantum.h"
#include "rgblight.h"
#include "drivers/led/issi/is31fl3731.h"
#include "i2c_master.h"


#ifndef RGBLIGHT_ENABLE
#error "MUST enable rgblight"
#endif
// rgb ring leds setting

const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = {
/* Refer to IS31 manual for these locations
 *   driver
 *   |  R location
 *   |  |       G location
 *   |  |       |       B location
 *   |  |       |       | */
    {0, C1_1,   C3_2,   C4_2},
    {0, C1_2,   C2_2,   C4_3},
    {0, C1_3,   C2_3,   C3_3},
    {0, C1_4,   C2_4,   C3_4},
    {0, C1_5,   C2_5,   C3_5},
    {0, C1_6,   C2_6,   C3_6},
    {0, C1_7,   C2_7,   C3_7},
    {0, C1_8,   C2_8,   C3_8},

    {0, C9_1,   C8_1,   C7_1},
    {0, C9_2,   C8_2,   C7_2},
    {0, C9_3,   C8_3,   C7_3},
    {0, C9_4,   C8_4,   C7_4},
    {0, C9_5,   C8_5,   C7_5},
    {0, C9_6,   C8_6,   C7_6},
    {0, C9_7,   C8_7,   C6_6},
    {0, C9_8,   C7_7,   C6_7},

    {0, C1_9,   C3_10,  C4_10},
    {0, C1_10,  C2_10,  C4_11},
    {0, C1_11,  C2_11,  C3_11},
    {0, C1_12,  C2_12,  C3_12},
};

#define RING_OUTER_BEGIN    0
#define RING_OUTER_END      15
#define RING_OUTER_SIZE     (RING_OUTER_END + 1 - RING_OUTER_BEGIN)

#define RING_INNER_BEGIN    16
#define RING_INNER_END      19
#define RING_INNER_SIZE     (RING_INNER_END + 1 - RING_INNER_BEGIN)

#define SPEED_MAX           100
#define SPEED_STEP          10

typedef enum {
    RING_STATE_INIT,
    RING_STATE_QMK,
    RING_STATE_CUSTOM,
} RING_STATE;

typedef enum {
    RING_EFFECT_1,
    RING_EFFECT_2,
    RING_EFFECT_3,
    RING_EFFECT_4,
    RING_EFFECT_5,
    RING_EFFECT_6,
    RING_EFFECT_MAX
} RING_EFFECT;

typedef struct {
    uint8_t state;
    uint8_t effect;
    uint8_t speed;
    uint8_t outer_index;
    uint8_t inner_index;
    uint8_t effect_count;
    uint8_t led_begin;
    uint8_t led_end;
    bool    led_forward;
    bool    led_clear;
} rgb_ring_t;

static rgb_ring_t rgb_ring = {
    .state          = RING_STATE_INIT,
    .effect         = RING_EFFECT_1,
    .speed          = 10,
    .outer_index    = 0,
    .inner_index    = 0,
    .effect_count   = 0,
    .led_begin      = RING_OUTER_BEGIN,
    .led_end        = RING_OUTER_END,
    .led_forward    = true,
    .led_clear      = false,
};

static void rgb_ring_reset(void)
{
    rgb_ring.effect_count   = 0;
    rgb_ring.led_begin      = RING_OUTER_BEGIN;
    rgb_ring.led_end        = RING_OUTER_END;
    rgb_ring.led_forward    = true;
    rgb_ring.led_clear      = false;
}

extern animation_status_t animation_status;
extern rgblight_config_t rgblight_config;

#define EFFECT_TEST_INTERVAL    50
#define EFFECT_TEST_COUNT       5
#define EFFECT_TEST_HUE_STEP    85
#define EFFECT_TEST_VAL_STEP    17
static void testing_mode(void)
{
    if (timer_elapsed(animation_status.last_timer) > EFFECT_TEST_INTERVAL) {
        HSV h = {rgblight_config.hue, rgblight_config.sat, rgblight_config.val};
        RGB c = hsv_to_rgb(h);
        //IS31FL3731_set_color_all(c.r, c.g, c.b);
        IS31FL3731_set_color_all(0, 0, 0);
        IS31FL3731_set_color(rgb_ring.outer_index+RING_OUTER_BEGIN, c.r, c.g, c.b);
        h.v = EFFECT_TEST_VAL_STEP*rgb_ring.outer_index;
        c = hsv_to_rgb(h);
        for (uint8_t i = RING_INNER_BEGIN; i <= RING_INNER_END; i++) {
            IS31FL3731_set_color(i, c.r, c.g, c.b);
        }
        rgb_ring.outer_index = (rgb_ring.outer_index + 1) % RING_OUTER_SIZE;
        //rgb_ring.inner_index = (rgb_ring.inner_index + 1) % RING_INNER_SIZE;

        if (rgb_ring.outer_index == RING_OUTER_BEGIN) {
            rgblight_config.hue += EFFECT_TEST_HUE_STEP;
            rgb_ring.effect_count++;
        }
        animation_status.last_timer = timer_read();
    }
    if (rgb_ring.effect_count > EFFECT_TEST_COUNT) {
        rgb_ring_reset();
        rgb_ring.state = RING_STATE_QMK;
        rgblight_set();
    }
}

static bool need_update(uint32_t max_interval)
{
    uint32_t interval = timer_elapsed(animation_status.last_timer);
    return (interval*rgb_ring.speed) > max_interval;
}

static void update_effect(uint32_t max_count)
{
    if (rgb_ring.effect_count > max_count) {
        rgb_ring_reset();
        rgb_ring.effect = (rgb_ring.effect + 1) % RING_EFFECT_MAX;
    }
}

#define EFFECT_1_INTERVAL  1000
#define EFFECT_1_COUNT     64
#define EFFECT_1_HUE_STEP  15

static void ring_effect_no_1(void)
{
    if (need_update(EFFECT_1_INTERVAL)) {
        HSV h = {rgblight_config.hue, rgblight_config.sat, rgblight_config.val};
        for (uint8_t i = RING_OUTER_BEGIN; i <= RING_OUTER_END; i++) {
            RGB c = hsv_to_rgb(h);
            IS31FL3731_set_color(i, c.r, c.g, c.b);
        }
        rgblight_config.hue += EFFECT_1_HUE_STEP;
        rgb_ring.effect_count++;
        animation_status.last_timer = timer_read();
    }

    update_effect(EFFECT_1_COUNT);
}

#define EFFECT_2_INTERVAL  1000
#define EFFECT_2_COUNT     64
#define EFFECT_2_HUE_STEP  15

static void ring_effect_no_2(void)
{
    if (need_update(EFFECT_2_INTERVAL)) {
        IS31FL3731_set_color_all(0, 0, 0);
        HSV h = {rgblight_config.hue, rgblight_config.sat, rgblight_config.val};
        RGB c = hsv_to_rgb(h);

        IS31FL3731_set_color(rgb_ring.led_begin, c.r, c.g, c.b);
        IS31FL3731_set_color(rgb_ring.led_end, c.r, c.g, c.b);

        rgb_ring.led_begin = (rgb_ring.led_begin + 1) % RING_OUTER_SIZE;
        rgb_ring.led_end = (rgb_ring.led_end + RING_OUTER_SIZE - 1) % RING_OUTER_SIZE;

        rgblight_config.hue += EFFECT_2_HUE_STEP;
        rgb_ring.effect_count++;
        animation_status.last_timer = timer_read();
    }

    update_effect(EFFECT_2_COUNT);
}

#define EFFECT_3_INTERVAL  1000
#define EFFECT_3_COUNT     64
#define EFFECT_3_HUE_STEP  15

static void ring_effect_no_3(void)
{
    if (rgb_ring.effect_count == 0) {
        IS31FL3731_set_color_all(0, 0, 0);
    }

    if (need_update(EFFECT_3_INTERVAL)) {
        HSV h = {rgblight_config.hue, rgblight_config.sat, rgblight_config.val};

        if (rgb_ring.led_clear) {
            IS31FL3731_set_color(rgb_ring.led_begin, 0, 0, 0);
            IS31FL3731_set_color(rgb_ring.led_end, 0, 0, 0);
        } else {
            RGB c = hsv_to_rgb(h);
            IS31FL3731_set_color(rgb_ring.led_begin, c.r, c.g, c.b);
            IS31FL3731_set_color(rgb_ring.led_end, c.r, c.g, c.b);
        }

        rgb_ring.led_begin = (rgb_ring.led_begin + 1) % RING_OUTER_SIZE;
        if (rgb_ring.led_begin == rgb_ring.led_end) {
            if (rgb_ring.led_forward) {
                rgb_ring.led_begin = RING_OUTER_BEGIN;
                rgb_ring.led_end = RING_OUTER_END+1;
            } else {
                rgb_ring.led_begin = RING_OUTER_BEGIN + RING_OUTER_SIZE/2;
                rgb_ring.led_end = RING_OUTER_END+1 - RING_OUTER_SIZE/2;
            }

            if (!rgb_ring.led_clear) {
                rgb_ring.led_forward = !rgb_ring.led_forward;
            }

            rgb_ring.led_clear = !rgb_ring.led_clear;
        }

        rgb_ring.led_end = (rgb_ring.led_end + RING_OUTER_SIZE - 1) % RING_OUTER_SIZE;

        rgblight_config.hue += EFFECT_3_HUE_STEP;
        rgb_ring.effect_count++;
        animation_status.last_timer = timer_read();
    }

    update_effect(EFFECT_3_COUNT);
}

#define EFFECT_4_INTERVAL  1000
#define EFFECT_4_COUNT     64
#define EFFECT_4_STEP      3
static void ring_effect_no_4(void)
{
    if (need_update(EFFECT_4_INTERVAL)) {
        IS31FL3731_set_color_all(0, 0, 0);
        HSV h = {rgblight_config.hue, rgblight_config.sat, rgblight_config.val};
        RGB c = hsv_to_rgb(h);

        IS31FL3731_set_color(rgb_ring.led_begin, c.r, c.g, c.b);
        IS31FL3731_set_color(rgb_ring.led_end, c.r, c.g, c.b);

        rgb_ring.led_begin = (rgb_ring.led_begin + EFFECT_4_STEP) % RING_OUTER_SIZE;
        rgb_ring.led_end = (rgb_ring.led_end + RING_OUTER_SIZE - EFFECT_4_STEP) % RING_OUTER_SIZE;

        rgblight_config.hue += EFFECT_1_HUE_STEP;
        rgb_ring.effect_count++;
        animation_status.last_timer = timer_read();
    }

    update_effect(EFFECT_4_COUNT);
}

#define EFFECT_5_INTERVAL  1000
#define EFFECT_5_COUNT     64
#define EFFECT_5_HUE_STEP  16
static void ring_effect_no_5(void)
{
    if (need_update(EFFECT_5_INTERVAL)) {
        IS31FL3731_set_color_all(0, 0, 0);
        for (uint8_t i = RING_INNER_BEGIN; i <= RING_INNER_END; i++) {
            HSV h = {rgblight_config.hue, rgblight_config.sat, rgblight_config.val};
            RGB c = hsv_to_rgb(h);
            IS31FL3731_set_color(i, c.r, c.g, c.b);
        }
        for (uint8_t i = RING_OUTER_BEGIN; i <= RING_OUTER_END; i++) {
            HSV h = {rgblight_config.hue+EFFECT_5_HUE_STEP, rgblight_config.sat, rgblight_config.val};
            RGB c = hsv_to_rgb(h);
            IS31FL3731_set_color(i, c.r, c.g, c.b);
        }
        rgblight_config.hue += EFFECT_5_HUE_STEP;
        rgb_ring.effect_count++;
        animation_status.last_timer = timer_read();
    }

    update_effect(EFFECT_5_COUNT);
}

#define EFFECT_6_INTERVAL  1000
#define EFFECT_6_COUNT     64
#define EFFECT_I_HUE_STEP  10
#define EFFECT_O_HUE_STEP  10
static void ring_effect_no_6(void)
{
    if (need_update(EFFECT_6_INTERVAL)) {
        IS31FL3731_set_color_all(0, 0, 0);
        for (uint8_t i = RING_INNER_BEGIN; i <= RING_INNER_END; i++) {
            HSV h = {rgblight_config.hue+i*EFFECT_I_HUE_STEP, rgblight_config.sat, rgblight_config.val};
            RGB c = hsv_to_rgb(h);
            IS31FL3731_set_color(i, c.r, c.g, c.b);
        }
        for (uint8_t i = RING_OUTER_BEGIN; i <= RING_OUTER_END; i++) {
            HSV h = {rgblight_config.hue+i*EFFECT_O_HUE_STEP, rgblight_config.sat, rgblight_config.val};
            RGB c = hsv_to_rgb(h);
            IS31FL3731_set_color(i, c.r, c.g, c.b);
        }
        rgblight_config.hue += EFFECT_I_HUE_STEP;
        rgb_ring.effect_count++;
        animation_status.last_timer = timer_read();
    }

    update_effect(EFFECT_6_COUNT);
}

typedef void(*effect_fun)(void);
static effect_fun effect_funcs[RING_EFFECT_MAX] = {
    ring_effect_no_1,
    ring_effect_no_2,
    ring_effect_no_3,
    ring_effect_no_4,
    ring_effect_no_5,
    ring_effect_no_6,
};

static void custom_effects(void)
{
    effect_funcs[rgb_ring.effect]();
}

void rgblight_call_driver(LED_TYPE *start_led, uint8_t num_leds)
{
    if (rgb_ring.state != RING_STATE_QMK) {
        return;
    }

    for (uint8_t i = 0; i < num_leds; i++) {
        IS31FL3731_set_color(i, start_led[i].r, start_led[i].g, start_led[i].b);
    }
}


void rgb_ring_init(void)
{
    i2c_init();
    IS31FL3731_init(DRIVER_ADDR_1);
    for (int index = 0; index < DRIVER_LED_TOTAL; index++) {
        bool enabled = true;
        IS31FL3731_set_led_control_register(index, enabled, enabled, enabled);
    }
    IS31FL3731_update_led_control_registers(DRIVER_ADDR_1, 0);
}

void rgb_ring_task(void)
{
    switch (rgb_ring.state) {
        case RING_STATE_INIT: // testing mode
            testing_mode();
            break;
        case RING_STATE_QMK: // qmk effects
            //rgblight_task();
            break;
        case RING_STATE_CUSTOM: // custom effects
            custom_effects();
            break;
        default:
            break;
    };

    IS31FL3731_update_pwm_buffers(DRIVER_ADDR_1, 0);
}

bool process_record_kb(uint16_t keycode, keyrecord_t *record)
{
    if (record->event.pressed) {
        switch(keycode) {
            case RGB_MODE_FORWARD:
                if (rgb_ring.state == RING_STATE_INIT) {
                    // in testing mode, do nothing
                    return false;
                } else if (rgb_ring.state == RING_STATE_CUSTOM) {
                    // switch to qmk mode
                    rgblight_config.mode = 1;
                    rgb_ring.state = RING_STATE_QMK;
                    rgblight_mode(rgblight_config.mode);
                    return false;
                } else {
                    // in qmk mode, switch to custom mode?
                    if (rgblight_config.mode >= RGBLIGHT_MODES) {
                        rgb_ring.state = RING_STATE_CUSTOM;
                        return false;
                    }
                }
                break;
            case RGB_MODE_REVERSE:
                if (rgb_ring.state == RING_STATE_INIT) {
                    // in testing mode, do nothing
                    return false;
                } else if (rgb_ring.state == RING_STATE_CUSTOM) {
                    // switch to qmk mode
                    rgblight_config.mode = RGBLIGHT_MODES;
                    rgb_ring.state = RING_STATE_QMK;
                    rgblight_mode(rgblight_config.mode);
                    return false;
                } else {
                    // in qmk mode, switch to custom mode?
                    if (rgblight_config.mode <= 1) {
                        rgb_ring.state = RING_STATE_CUSTOM;
                        return false;
                    }
                }
                break;
            case KC_F24:
                if (rgb_ring.state == RING_STATE_QMK) {
                    rgb_ring.state = RING_STATE_CUSTOM;
                    rgb_ring_reset();
                    return false;
                } else if (rgb_ring.state == RING_STATE_CUSTOM) {
                    rgb_ring.state = RING_STATE_QMK;
                    return false;
                }
                break;
            default:
                break;
        }
    }
    return process_record_user(keycode, record);
}
