/*
 * rgbprog_rainbow.c
 * Oct 26, 2024
 *
 * rgb mode has a few submodes:
 * - standard rgb which shows the entire color wheel
 * - split top bottom where bottom is either a mirror or a rotation of the top
 */

#include <stdint.h>
#include "rgbled.h"



static const uint8_t str_hue_angle[] = "Hue Angle";
static const uint8_t str_direction[] = "Direction";



const uint8_t * rgb_rainbow_desc(uint8_t idx)
{
    switch (idx) {
        case 0:
        case 1:  return RGB_STR_INCREMENT;
        case 2:  return str_hue_angle;
        case 3:  return str_direction;
        case 4:
        case 5:  return RGB_STR_DELAY;
        case 6:
        case 7:  return RGB_STR_UNUSED;
        case 14:
        case 15: return RGB_STR_BRIGHTNESS;
        default:
            return RGB_STR_RESERVED;
    }
}

static uint8_t timeout;



void rgb_rainbow(uint8_t *a, uint16_t tick)
{
    // 0=angle-rate, 1=bitfield, 2=timeout-set, 45=angle-work, 6=sat, 7=val
    // bitfield:
    // 7:4 hue angle division (360 divided by 1-6; all other values mean 1, except 0 which means no offset)
    // 3
    // 2
    // 1
    // 0 direction

    uint16_t *rb_angle = (uint16_t *)&a[4];
    uint16_t angle;
    uint16_t hoffset;
    int i;
    int r;

    // timeout
    if (!timeout || (timeout > a[2])) {
        timeout = a[2];
    } else {
        timeout--;
        return;
    }

    // rainbow hue angle increment
    // todo: in single mode, decrement instead if direction bit set
    *rb_angle += (a[0]);
    if (*rb_angle >= 0x600) *rb_angle -= 0x600;
    angle = *rb_angle;

    // hue offset
    switch (a[1] >> 4) {
        case 0:     { hoffset = 0; break; }     // time stands still
        case 2:     { hoffset = 0x4d; break; }  // 0xa * 0x4d * 2 = 0x604
        case 3:     { hoffset = 0x33; break; }  // 0xa * 0x33 * 3 = 0x5fa
        case 4:     { hoffset = 0x26; break; }  // 0xa * 0x26 * 4 = 0x5f0
        case 5:     { hoffset = 0x1f; break; }  // 0xa * 0x1f * 5 = 0x60e
        case 6:     { hoffset = 0x1a; break; }  // 0xa * 0x1a * 6 = 0x618
        default:    { hoffset = 0x9a; break; }  // 0xa * 0x9a * 1 = 0x604
    }

    // apply to LEDs
    for (i = 0; i < RGBLED_COUNT; i++) {
        r = (a[1] & 0x01) ? i : RGBLED_COUNT - 1 - i;
        hsv_out[rgb_map[r]].h = angle;
        hsv_out[rgb_map[r]].s = a[6];
        hsv_out[rgb_map[r]].v = a[7];

        angle += hoffset;
        if (angle >= 0x600) angle -= 0x600;
    }
}
