#include <stdlib.h>

#include "pico/stdlib.h"
#include "tusb.h"
#include "hardware/adc.h"

#include "hyperx_elite2.h"

static absolute_time_t lastSend;
static absolute_time_t lastRead;
static bool backlight = false;
static uint16_t adc_value = 0;
static bool mute = false;

static const unsigned int SKIP_INDICES[] = { 23, 29, 41, 47, 70, 71, 76, 87, 88, 93, 99, 100, 102, 108, 113 };

static unsigned char buf[BUF_SIZE];
static uint8_t buf_idx=0;
static uint8_t packets_sent=0;
static uint8_t color_idx = 0;
static uint8_t skipped = 0;
const unsigned int* skip_idx = &SKIP_INDICES[0];

static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blue);
static void send_initial(uint8_t dev_addr);

void get_light() {
	// get ADC reading from LDR every 500ms
	// if above threshold, set backlight to off
	if ( absolute_time_diff_us(lastRead, get_absolute_time()) >= 500000) {
		adc_value = adc_read();
		if (backlight && adc_value >= LDR_OFF_THRESHOLD) {
			backlight = false;
		} else if (!backlight && adc_value <= LDR_ON_THRESHOLD) {
			backlight = true;
		}
	}
}

void rgb_task(uint8_t dev_addr) {
	// the RGB protocol used by HyperX sends individual key RGB data in
	// multiple packets
	// the code here will determine if we are in the middle of sending
	// updated color info (packets_sent>0) and continue to send the next
	// packet if so at a rate of one packet every 20ms
	// otherwise, wait 0.5s before sending the next set of color packets
	if (packets_sent>0) {
		if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= 20000) {
			if( backlight) {
				send_color(dev_addr, 0x20, 0x20, 0x20);
				// send a dim white color (#202020) for all keys 
			} else{
				send_color(dev_addr, 0x00, 0x00, 0x00);
				// turn off all lighting by sending (#000000) for all keys
			}
			lastSend = get_absolute_time();
		}
	} else {
		if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= 500000) {
			send_initial(dev_addr);
			lastSend = get_absolute_time();
		}
	}
}

// send an individual color packett with the desired RGB color
static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blue) {
	memset(buf, 0x00, BUF_SIZE);

	buf_idx = 0;
 
	// check if there are still keys left to send and send updated colors
	// for those keys
	// each key gets 4 bytes - an init byte (0x81) plus 3 bytes for RGB
	if(color_idx < NUM_KEYS)
	{
		while(color_idx < NUM_KEYS && buf_idx < BUF_SIZE){
			if(*skip_idx == color_idx + skipped)
			{
				// keys in skip_idx are not assigned to a key, so send all 0x00
				buf[buf_idx]		= 0x00;
				buf[buf_idx + 1]	= 0x00;
				buf[buf_idx + 2]	= 0x00;
				buf[buf_idx + 3]	= 0x00;
        
				skip_idx++;

				if(skip_idx >= SKIP_INDICES + sizeof(SKIP_INDICES) / sizeof(unsigned int))
				{
					skip_idx = SKIP_INDICES;
				}
				skipped++;
			} else {
				// start by sending color init byte for the current key
				buf[buf_idx]		= 0x81;
				// turn rewind, play, and fast forward keys to white
				if(color_idx==105 || color_idx==108 || color_idx==109) {
					buf[buf_idx + 1]	= 0x20;
					buf[buf_idx + 2]	= 0x20;
					buf[buf_idx + 3]	= 0x20;
				} else if(color_idx==99) {
				// set color of mute button to green or red based on mute
				// toggle - note that this toggle is not synced to the PC's 
				// audio state and is presumed to be unmuted when the keyboard
				// first receives power
					if(mute) {
						buf[buf_idx + 1]	= 0x40;
						buf[buf_idx + 2]	= 0x00;
						buf[buf_idx + 3]	= 0x00;
					} else {
						buf[buf_idx + 1]	= 0x00;
						buf[buf_idx + 2]	= 0x40;
						buf[buf_idx + 3]	= 0x00;
					}
				} else {
					// for a normal key, send the desired RGB colors
					buf[buf_idx + 1]	= red;
					buf[buf_idx + 2]	= green;
					buf[buf_idx + 3]	= blue;
				}
				color_idx++;
			}
			buf_idx += 4;
		}

		if(tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
		{
			// packet sent successfully, increment
			packets_sent++;
		}
	} else {
		if(packets_sent < NUM_PACKETS)
		{
			// all keys have been sent, but the protocol expects NUM_PACKETS
			// packets to be sent in total; if we have not sent enough packets,
			// send extra packets of all 0x00 until done
			if(tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
			{
				packets_sent++;
			}
		} else {
			// a full round of color packets completed, reset packets to 0
			packets_sent=0;
		}
	}
}

// send the special initialization  packet that tells the keyboard to expect 
// color packets to follow
static void send_initial(uint8_t dev_addr) {
	memset(buf, 0x00, BUF_SIZE);

	color_idx = 0;
	skipped = 0;
	skip_idx = &SKIP_INDICES[0];

	// send initialization packet
	buf[0x00] = 0x04;
	buf[0x01] = 0xf2;

	if (tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
	{
		// we have begun sending packets, so indicate first packet was sent
		// remaining packets will be sent at regular intervals
		packets_sent=1;
	}
}

// initialize the ADC for reading the LDR
void startADC() {
	stdio_init_all();
	adc_init();
	adc_gpio_init(LDR_PIN);
	adc_select_input(LDR_ADC);
}

// forward HID report after processing
bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len) {
	if (instance == 0x01 && report[0] == 0x03 && report[1] == 0xE2) {
		mute = !mute;
	}

	return tud_hid_n_report(instance, 0, report, len);
}
