/*
 * $Id: rgbled_prog.c 464 2019-08-01 02:15:54Z true $
 * begin 20190616 true
 *
 * programs to run to show neat shit on the LEDs
 */


#include "led/rgbled.h"

#include "sensor/adxl.h"

#include "math/rand16.h"
#include "math/sin7.h"


#define LEDPROG_BUFLEN		9


static uint8_t idata led_tick;

static uint8_t idata led_idx;
static uint8_t xdata led_aux;


void ledprog_none(uint8_t *prog)
{
	(prog = prog);
}


void ledprog_solid(uint8_t *prog)
{
	uint8_t i;
	uint8_t flags = prog[0] >> 4;

	// f: set = individual colors, unset = single color
	// 1: led1 hue, or all hue
	// 2: led1 sat, or all sat
	// 3: led1 val, or all val
	// 4: led2 hue...

	for (i = 0; i < RGBLED_COUNT; i++) {
		if (flags) {
			hsv[i].h = prog[1 + (i * 2)] * 6;
			hsv[i].s = prog[2 + (i * 2)];
			hsv[i].v = prog[3 + (i * 2)];
		} else {
			hsv[i].h = prog[1] * 6;
			hsv[i].s = prog[2];
			hsv[i].v = prog[3];
		}
	}
}

void ledprog_solid_edit(uint8_t state)
{
	switch (state) {
		case 0: {

		}
	}
}

void ledprog_flicker(uint8_t *prog)
{
	uint8_t i;
	uint8_t flags = prog[0] >> 4;
	uint8_t rnd, rnd_first;

	// f: set = individual colors, unset = single color
	// 1: led1 hue, or all hue
	// 2: led1 sat, or all sat
	// 3: led1 val, or all val
	// 4: led2 hue...
	// ...
	// 12: led4 val
	// 13: bits 3..0: set = individual flicker, unset = constant flicker

	led_tick++;
	if (led_tick % 32 == 0) {		// hardcoded; use a variable as speed later
		rnd_first = rand16() & 0x0e;

		for (i = 0; i < RGBLED_COUNT; i++) {
			rnd = (prog[13] & (i << 1)) ? (rand16() & 0x0e) : rnd_first;

			if (flags & (1 << i)) {
				hsv[i].h = prog[1 + (i * 2)] * 6;
				hsv[i].s = prog[2 + (i * 2)];
				hsv[i].v = (prog[3 + (i * 2)] & 0xf1) | rnd;
			} else {
				hsv[i].h = prog[1] * 6;
				hsv[i].s = prog[2];
				hsv[i].v = (prog[3] & 0xf1) | rnd;
			}
		}
	}
}

void ledprog_circler(uint8_t *prog)
{
	uint8_t i;
	uint8_t offset;
	uint8_t idx;

	// f: bit 0: set=clockwise, unset=counterclockwise
	// f: bit 1:
	// 1: fg hue
	// 2: fg sat
	// 3: fg val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 7: fading rate
	// 8: circle speed

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;

		led_idx++;
		led_idx %= 4;

		for (i = 0; i < RGBLED_COUNT; i++) {
			idx = (prog[0] & 0x01) ? (3 - led_idx) : led_idx;

			// todo: implement fading
			offset = (i == idx) ? 3 : 0;

			hsv[i].h = prog[1 + offset] * 6;
			hsv[i].s = prog[2 + offset];
			hsv[i].v = prog[3 + offset];
		}
	}
}

void ledprog_random(uint8_t *prog)
{
	uint8_t i;
	uint8_t offset;
	uint8_t rnd, rnd_first;

	uint8_t flags = prog[0] >> 4;

	// 1: fg hue
	// 2: fg sat
	// 3: fg val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 8: rando speed
	// 12: rando threshold
	// 13: bits 3..0: set = individual flicker, unset = constant flicker

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;

		rnd_first = rand16() & 0xff;

		for (i = 0; i < RGBLED_COUNT; i++) {
			rnd = (prog[13] & (i << 1)) ? (rand16() & 0xff) : rnd_first;

			offset = (rnd < prog[12]) ? 3 : 0;

			hsv[i].h = prog[1 + offset] * 6;
			hsv[i].s = prog[2 + offset];
			hsv[i].v = prog[3 + offset];
		}
	}
}

void ledprog_randomcolor(uint8_t *prog)
{
	uint8_t i;
	uint8_t valid;
	uint8_t rnd, rnd_first;

	uint8_t flags = prog[0] >> 4;

	// 3: all val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 8: rando speed
	// 12: rando threshold
	// 13: bits 3..0: set = individual flicker, unset = constant flicker

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;

		rnd_first = rand16() & 0xff;

		for (i = 0; i < RGBLED_COUNT; i++) {
			rnd = (prog[13] & (i << 1)) ? (rand16() & 0xff) : rnd_first;

			valid = (rnd < prog[12]) ? 3 : 0;

			hsv[i].h = valid ? ((rand16() & 0xff) * 6)    : prog[1] * 6;
			hsv[i].s = valid ? ((rand16() & 0x0f) | 0xf0) : prog[2];
			hsv[i].v = prog[3];

			// about 1/16 of the time, make the color more white
			if (hsv[i].s == 0xf0) {
				hsv[i].s -= 96;
			}
		}
	}
}

void ledprog_ccross(uint8_t *prog)
{
	uint8_t i;
	uint8_t offset;

	uint8_t flags = prog[0] >> 4;

	// 1: fg hue
	// 2: fg sat
	// 3: fg val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 8: speed

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;

		led_idx++;
		led_idx %= 2;

		for (i = 0; i < RGBLED_COUNT; i++) {
			offset = ((i % 2) == led_idx) ? 3 : 0;

			hsv[i].h = prog[1 + offset] * 6;
			hsv[i].s = prog[2 + offset];
			hsv[i].v = prog[3 + offset];
		}
	}
}

void ledprog_cflip(uint8_t *prog)
{
	uint8_t i;
	uint8_t offset;

	uint8_t flags = prog[0] >> 4;

	// 1: fg hue
	// 2: fg sat
	// 3: fg val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 8: speed

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;

		led_idx++;
		led_idx %= 2;

		for (i = 0; i < RGBLED_COUNT; i+= 2) {
			offset = ((i / 2) == led_idx) ? 3 : 0;

			hsv[i].h = hsv[i+1].h = prog[1 + offset] * 6;
			hsv[i].s = hsv[i+1].s = prog[2 + offset];
			hsv[i].v = hsv[i+1].v = prog[3 + offset];
		}
	}
}

void ledprog_cmirror(uint8_t *prog)
{
	uint8_t i;
	uint8_t offset;

	uint8_t flags = prog[0] >> 4;

	// 1: fg hue
	// 2: fg sat
	// 3: fg val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 8: speed

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;

		led_idx++;
		led_idx %= 2;

		for (i = 0; i < RGBLED_COUNT; i+= 2) {
			offset = ((i / 2) == led_idx) ? 3 : 0;

			hsv[i+1].h = hsv[(i+2) % RGBLED_COUNT].h = prog[1 + offset] * 6;
			hsv[i+1].s = hsv[(i+2) % RGBLED_COUNT].s = prog[2 + offset];
			hsv[i+1].v = hsv[(i+2) % RGBLED_COUNT].v = prog[3 + offset];
		}
	}
}

void ledprog_calt(uint8_t *prog)
{
	uint8_t i;
	uint8_t offset;

	uint8_t flags = prog[0] >> 4;

	// 1: fg hue
	// 2: fg sat
	// 3: fg val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 8: speed

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;

		led_idx++;
		led_idx %= 2;

		offset = led_idx ? 3 : 0;

		for (i = 0; i < RGBLED_COUNT; i+= 2) {
			hsv[i].h = hsv[i+1].h = prog[1 + offset] * 6;
			hsv[i].s = hsv[i+1].s = prog[2 + offset];
			hsv[i].v = hsv[i+1].v = prog[3 + offset];
		}
	}
}

void ledprog_pfade1(uint8_t *prog)
{
	uint8_t i;
	uint8_t fade;

	uint8_t flags = prog[0] >> 4;

	// f: set = individual colors, unset = single color
	// 1: led1 hue, or all hue
	// 2: led1 sat, or all sat
	// 3: led1 val, or all val
	// 4: led2 hue...

	led_tick++;
	if (led_tick >= prog[13]) {
		led_tick = 0;
		led_idx++;

		fade = 0;

		if (led_idx < 64) {
			fade = led_idx << 2;
		} else if (led_idx < 96) {
			fade = 255;
		} else if (led_idx < 112) {
			fade = 255 - ((led_idx - 96) << 4);
		} else if (led_idx < 144) {
			// off
		} else {
			led_idx = 0;
		}

		for (i = 0; i < RGBLED_COUNT; i++) {
			if (flags) {
				hsv[i].h = prog[1 + (i * 2)] * 6;
				hsv[i].s = prog[2 + (i * 2)];
				hsv[i].v = (prog[3 + (i * 2)] * fade) >> 8;
			} else {
				hsv[i].h = prog[1] * 6;
				hsv[i].s = prog[2];
				hsv[i].v = (prog[3] * fade) >> 8;
			}
		}
	}
}

void ledprog_rainbow(uint8_t *prog)
{
	uint8_t i;
	uint8_t hue, range;

	// todo: blend between different sat/val as well

	// 0: bit 0: unset = single direction, set = reverse direction
	// 0: bit 1: set = auto-reverse (ignore bit 0)
	// 1: main hue start
	// 2: all sat
	// 3: all val
	// 4: sub hue
	// 8: update rate
	// 9: led1 hue offset
	// 10: led2 hue offset
	// 11: led3 hue offset
	// 12: led4 hue offset

	led_tick++;
	if (led_tick >= prog[8]) {
		led_tick = 0;
		led_idx++;

		if (prog[4] > prog[1]) {
			range = prog[4] - prog[1];
			hue = range + prog[1];
		} else {
			range = prog[1] - prog[4];
			hue = range + prog[1];
		}

		if (led_idx >= range) {
			led_idx = 0;
			led_aux = !led_aux;
		}

		// auto-reverse
		if (prog[0] & 0x02) {
			if (led_aux) {
				// reverse
				hue += (range - led_idx);
			} else {
				// just normal increment mode
				hue += led_idx;
			}
		} else {
			if (prog[0] & 0x01) {
				// reverse
				hue += (range - led_idx);
			} else {
				// just normal increment mode
				hue += led_idx;
			}
		}

		for (i = 0; i < RGBLED_COUNT; i++) {
			hsv[i].h = (uint16_t)((uint8_t)(hue + prog[9 + i])) * 6;
			hsv[i].s = prog[2];
			hsv[i].v = prog[3];
		}
	}
}

/*
void ledprog_accel_heavycolor()
{
	uint8_t i;

	uint16_t blend_h;
	uint8_t blend_s, blend_v;

	// 1: fg hue
	// 2: fg sat
	// 3: fg val
	// 4: bg hue
	// 5: bg sat
	// 6: bg val
	// 11: threshold for strongest value

	for (i = 0; i < RGBLED_COUNT; i+= 2) {
		hsv[i].h = hsv[i+1].h = prog[1 + offset] * 6;
		hsv[i].s = hsv[i+1].s = prog[2 + offset];
		hsv[i].v = hsv[i+1].v = prog[3 + offset];
	}
}
*/


// with current code, cannot exceed 16 programs
LedProgram code ledprog[15] = {
	{"Solid Color",          &ledprog_solid,       &ledprog_solid_edit},
	{"Candle Flicker",       &ledprog_flicker,     &ledprog_solid_edit},
	{"Circle Chase (CCW)",   &ledprog_circler,     &ledprog_solid_edit},
	{"Circle Chase (CW)",    &ledprog_circler,     &ledprog_solid_edit},
	{"Random Alt Flicker",   &ledprog_random,      &ledprog_solid_edit},
	{"Rand Color Flicker",   &ledprog_randomcolor, &ledprog_solid_edit},
	{"Rand Color Always",    &ledprog_randomcolor, &ledprog_solid_edit},
	{"Color Cross",          &ledprog_ccross,      &ledprog_solid_edit},
	{"Color Flip",           &ledprog_cflip,       &ledprog_solid_edit},
	{"Color Mirror",         &ledprog_cmirror,     &ledprog_solid_edit},
	{"Color Alternate",      &ledprog_calt,        &ledprog_solid_edit},
	{"Ramping Pulse",        &ledprog_pfade1,      &ledprog_solid_edit},
	{"Rainbow Hue Blend",    &ledprog_rainbow,     &ledprog_solid_edit},
	{"Rainbow Blend Offset", &ledprog_rainbow,     &ledprog_solid_edit},
	{"Fav to Alt Hue Blend", &ledprog_rainbow,     &ledprog_solid_edit},
	//{"Blend Pass",  &ledprog_none,    &ledprog_solid_edit},	// blends from pri to alt color, left to right
	//{"Heavy Color",  &ledprog_none,    &ledprog_solid_edit},	// blends pri color at strongest accelerometer readings
};
