/*
 * rgbprog_copmode.c
 * Created Nov 1, 2024
 */

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

#include "user_config.h"



static uint8_t timeout;



const uint8_t * rgb_copmode_desc(uint8_t idx)
{
    switch (idx) {
        case 2:  return "Bitfield Hi";
        case 3:  return "Bitfield Lo";
        case 4:
        case 5:  return RGB_STR_DELAY;
        case 10: return "Pat2 Delay";
        case 11: return "Pat2 Cycles";
        case 12: return "Pat1 Delay";
        case 13: return "Pat1 Cycles";
        case 14:
        case 15: return RGB_STR_BRIGHTNESS;
        default:
            return RGB_STR_RESERVED;
    }
}


void rgb_copmode(uint8_t *a, uint16_t tick)
{
    // 0=work, 1=bitfield, 2=timeout-set, 3=timeout-work, 4=work, 56=steps, 7=val
    // bitfield:
    // 7 sync to tick 0
    // 6 skip pattern 2
    // 5 skip pattern 1
    // 4 don't override sat/val to maximums (not implemented)
    // 3 use different colors on either half during pattern 2
    // 2 use different colors on either half during pattern 1
    // 1 repeat subpattern 1
    // 0 use cop colors instead of pri/alt
    // others:
    // a[5][7:4] amount of time to delay in pattern 2
    // a[5][0:3] amount of times to repeat pattern 2 (solid color alternate)
    // a[6][7:4] amount of strobe pulses in pattern 1
    // a[6][0:3] amount of times to repeat pattern 1 (strobes)

    int i;

    uint8_t pattern, iter;
    uint8_t *seq;
    uint8_t w, x;
    uint8_t b;

    uint8_t cnt[4];

    color_hsv hsv;
    color_hsv hsv2;
    color_hsv pri, sec;

    // synchronize
    if (a[1] & 0x80) {
        if (!tick) {
            a[3] = 0;
            a[1] &= ~0x80;
        } else {
            return;
        }
    }

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

    // colors to use
    if (a[1] & 0x01) {
        pri.h = 0x000;
        pri.s = 0xff;
        sec.h = 0x400;
        sec.s = 0xff;
    } else {
        pri.h = uconf.favcolor_hue * 6;
        pri.s = uconf.favcolor_sat;
        sec.h = uconf.altcolor_hue * 6;
        sec.s = uconf.altcolor_sat;
    }

    // pattern mode
    pattern  = a[0] >> 6;
    iter     = a[0] & 0x3f;
    seq      = &a[4];

    cnt[0] = a[6] & 0xf;
    cnt[1] = a[6] >> 4;
    cnt[2] = a[5] & 0xf;
    cnt[3] = a[5] >> 4;

    if ((pattern == 0) && (a[1] & 0x20)) pattern++;
    if ((pattern == 1) && (a[1] & 0x40)) pattern++;

    if (pattern > 1) pattern = 0;

    switch (pattern) {
        // strobe
        case 0: {
            w = cnt[1] << 1;
            if (!w) w = 32;
            b = (a[1] & 0x02) ? 0x02 : 0x01;
            x = cnt[0];
            if (!x) x = 16;
            x <<= b;

            // set main color
            hsv.h = (iter & b) ? sec.h : pri.h;
            hsv.s = (iter & b) ? sec.s : pri.s;
            hsv.v = 0;

            // p1 alternate color mode
            if (a[1] & 0x04) {
                hsv2.h = (iter & b) ? pri.h : sec.h;
                hsv2.s = (iter & b) ? pri.s : sec.s;
            } else {
                hsv2.h = hsv.h;
                hsv2.s = hsv.s;
            }

            // set value if on
            if ((*seq < w) && !(*seq & 1)) {
                hsv.v = a[7];
            }

            if (*seq > w) {
                // iteration done
                iter++;
                *seq = 0;
                if (iter >= x) {
                    // pattern done
                    pattern++;
                    iter = 0;
                }

                break;
            }

            (*seq)++;
            break;
        }

        // solid alternate
        case 1: {
            w = cnt[3] << 1;
            if (!w) w = 32;
            x = cnt[2] << 1;
            if (!x) x = 32;

            // set main color
            hsv.h = (iter & 1) ? sec.h : pri.h;
            hsv.s = (iter & 1) ? sec.s : pri.s;
            hsv.v = 0;

            // p2 alternate color mode
            if (a[1] & 0x08) {
                hsv2.h = (iter & 1) ? pri.h : sec.h;
                hsv2.s = (iter & 1) ? pri.s : sec.s;
            } else {
                hsv2.h = hsv.h;
                hsv2.s = hsv.s;
            }

            // set value
            hsv.v = a[7];

            if (*seq > w) {
                // iteration done
                iter++;
                *seq = 0;
                if (iter >= x) {
                    // pattern done
                    pattern++;
                    iter = 0;
                }

                break;
            }

            (*seq)++;
            break;
        }
    }

    // we only have two patterns
    pattern %= 2;

    // save state
    a[0] = (pattern << 6) | (iter & 0x3f);

    // apply to LEDs
    w = RGBLED_COUNT/2;
    for (i = 0; i < w; i++) {
        hsv_out[rgb_map[i]].h = hsv.h;
        hsv_out[rgb_map[i]].s = hsv.s;
        hsv_out[rgb_map[i]].v = hsv.v;

        hsv_out[rgb_map[i+w]].h = hsv2.h;
        hsv_out[rgb_map[i+w]].s = hsv2.s;
        hsv_out[rgb_map[i+w]].v = hsv.v;
    }
}
