/*
 * led.cpp
 *
 * $Id$
*/


#include "led.h"

#include "hw/adc.h"


// need some direct assistance here
#include "soc/dport_reg.h"
#include "soc/ledc_reg.h"


uint16_t DRAM_ATTR led_spaceforce[10];		// uncorrected pwm value in order of SPACEFORCE
uint16_t DRAM_ATTR led_scaled[10];			// corrected after program runs

static uint8_t  led_state = 0;				// selected output(s), using 1-4
static uint16_t sr_state = 0;				// aux / other outputs on SR

static const uint8_t led_offset[4][3] = {
	{   7, 6, 5},
	{   2, 1, 0},
	{0xff, 8, 9},
	{0xff, 3, 4}
};


SPIClass *vspi;


void led_scale()
{
	double x;

	// todo: do something more expansive than this
	// return (((1<<(in/32))-1) + ((1<<(in/32))*((in%32)+1)+15)/32) << 3;
	
	// eh this sucks too
	for (int i = 0; i < 10; i++) {
		x = led_spaceforce[i] / 11.111111;
		x = (1.0 - cos(x * 3.14159 / 180.0)) * 2048.0;
		led_scaled[i] = (uint16_t)x;
	}
}


void led_init()
{
	// configure our scale constant
	//scale = (LED_PWM_MAX * log10(2)) / (log10(255));

	// disable LEDs until we're ready
	pinMode(LED_SR_OE_PIN, OUTPUT);
	digitalWrite(LED_SR_OE_PIN, HIGH);

	// configure pwms
	ledcSetup(LED_LEDC_BASE + 0, LED_PWM_FREQ, LED_PWM_BITS);
	ledcSetup(LED_LEDC_BASE + 1, LED_PWM_FREQ, LED_PWM_BITS);
	ledcSetup(LED_LEDC_BASE + 2, LED_PWM_FREQ, LED_PWM_BITS);

	// anode pwm channels
	ledcAttachPin(LED_PWM1_PIN, LED_LEDC_BASE + 0);
	ledcAttachPin(LED_PWM2_PIN, LED_LEDC_BASE + 1);
	ledcAttachPin(LED_PWM3_PIN, LED_LEDC_BASE + 2);

	// default off
	memset((uint8_t *)led_spaceforce, 0x00, sizeof(led_spaceforce));

	// spi config
	pinMode(LED_SR_LAT_PIN, OUTPUT);

	vspi = new SPIClass(VSPI);
	vspi->begin(18, 39, 23, 39);  // sck, miso, mosi, !ss
	vspi->setFrequency(16000000);
	vspi->setDataMode(SPI_MODE1);
}

void led_set(uint8_t led, uint16_t level)
{
	if (led < 10) {
		led_spaceforce[led] = level;
	}
}

uint16_t * led_getptr()
{
	return led_spaceforce;
}

void IRAM_ATTR led_tick()
{
	int i;
	uint32_t pwm[3];

	// calculate active row
	led_state++;
	if (led_state > 3) {
		led_state = 0;
	}

	// disable LED outputs
	digitalWrite(LED_SR_OE_PIN, HIGH);

	// latch and re-enable enabled output
	digitalWrite(LED_SR_LAT_PIN, LOW);
	vspi->transfer((1 << (led_state + 1)) | sr_state);
	digitalWrite(LED_SR_LAT_PIN, HIGH);

	// get PWMs
	for (i = 0; i < 3; i++) {
		if (led_offset[led_state][i] != 0xff) {
			pwm[i] = led_scaled[led_offset[led_state][i]];
		} else {
			pwm[i] = 0;
		}
	}
	
	// reset PWM to reduce visual jitter
	ledcWrite(LED_LEDC_BASE + 0, 0);
	ledcWrite(LED_LEDC_BASE + 1, 0);
	ledcWrite(LED_LEDC_BASE + 2, 0);

	// set PWMs to correct value	
	ledcWrite(LED_LEDC_BASE + 0, pwm[0]);
	ledcWrite(LED_LEDC_BASE + 1, pwm[1]);
	ledcWrite(LED_LEDC_BASE + 2, pwm[2]);

	// enable LED outputs again
	digitalWrite(LED_SR_OE_PIN, LOW);
}
