/*
 * human meat stub interface
 * 
 * note: the actual hw implementation of touch is super shit. like really super shit.
 * there could be phantom touches. keep this in mind and know you aren't going crazy.
 */


#include "ui.h"

#include "hw/hal/touch.h"


int16_t  touch[BTN_TOUCH_COUNT];
int16_t  touch_baseline[BTN_TOUCH_COUNT];

uint16_t btn_counts[BTN_TOUCH_COUNT];
BtnCb    btn_cb[BTN_TOUCH_COUNT];

uint8_t  calibrated;

//static const int8_t btn_touch_thresh[BTN_TOUCH_COUNT] = {12, 18, 16, 23}; // tested, safe above min value diff for a reliable touch at 0.7ms
static const int8_t btn_touch_thresh[BTN_TOUCH_COUNT] = {11, 16, 12, 21};	// tested, safe above min value diff for a reliable touch at 0.5ms

static uint8_t idx = 0;


static const uint8_t btn_pins[BTN_TOUCH_COUNT] = {
	BTN_TOUCH_1_PIN,
	BTN_TOUCH_2_PIN,
	BTN_TOUCH_SFONE_PIN,
	BTN_TOUCH_ERECTUS_PIN
};


void btn_init()
{	
	uint8_t i;

	for (i = 0; i < BTN_TOUCH_COUNT; i++) {
		btn_cb[i].push_cb = 0;
		btn_cb[i].held_cb = 0;
		btn_cb[i].release_cb = 0;
		
		btn_cb[i].held_cb_count = 100;

		touch[i] = 0;
		btn_counts[i] = 0;
	}

	calibrated = 0;

	touchSetCycles(0x800, 0x800);
	touchSetCyclesNB(0x800, 0x800);			// ~0.5ms per read

	//touchSetCycles(0xa00, 0xc00);
	//touchSetCyclesNB(0xa00, 0xc00);			// ~0.7ms per read

	btn_tick();
}

void btn_calibrate()
{
	uint8_t i, j;

	i = calibrated & 3;
	j = calibrated >> 2;

	switch (j) {
		case 0:
		case 1:
		case 2: {
			touch_baseline[i] = 0;
			break;
		}
		case 3:
		case 4:
		case 5: {
			touch_baseline[i] += touch[i];
			break;
		}
		case 6: {
			touch_baseline[i] += touch[i];
			touch_baseline[i] >>= 2;
			break;
		}
		default: {
			calibrated = 0xff;
			return;
		}
	}

	calibrated++;
}

uint8_t btn_get_calibrated()
{
	return (calibrated == 0xff ? 0xff : 0);
}

uint16_t btn_read_raw(uint8_t btn)
{
	return touch[btn];
}

uint16_t btn_read_counts(uint8_t btn)
{
	return btn_counts[btn];
}

uint16_t btn_read_baseline(uint8_t btn)
{
	return touch_baseline[btn];
}

void btn_set_cb(uint8_t btn, void (*push)(), void (*held)(), void (*release)())
{
	if (btn < 4) {
		btn_cb[btn].push_cb = push;
		btn_cb[btn].held_cb = held;
		btn_cb[btn].release_cb = release;
	}
}

void btn_cb_process()
{
	uint8_t i;

	for (i = 0; i < BTN_TOUCH_COUNT; i++) {
		// calculate last button status and call back if necessary
		if ((touch_baseline[i] - touch[i]) >= btn_touch_thresh[i]) {
			if (btn_counts[i] < (1 << ((sizeof(btn_counts[0]) * 8) - 1))) {
				btn_counts[i]++;
				if (btn_cb[i].push_cb && btn_counts[i] == BTN_PUSH_DEBOUNCE_COUNTS) {
					btn_cb[i].push_cb();
				}
				if (btn_cb[i].held_cb && btn_counts[i] == btn_cb[i].held_cb_count) {
					btn_cb[i].held_cb();
				}
			}
		} else if (btn_counts[i]) {
			btn_counts[i] = 0;
			if (btn_cb[i].release_cb) {
				btn_cb[i].release_cb();
			}
		}
	}
}

void IRAM_ATTR btn_tick()
{
	uint8_t t = 0;
	
	// get last value
	if (calibrated) {
		t = touchReadGet();
	}

	// is the reading valid / exists?
	// TODO: check to see if bug exists where sometimes we get no reading
	if (t) {
		// process and smooth
		touch[idx] += t;
		touch[idx] >>= 1;
	}

	// prepare for next button
	idx++;
	if (idx > BTN_TOUCH_COUNT) {
		idx = 0;
	}

	// start conversion on next button
	touchReadNB(btn_pins[idx]);
	
	if (calibrated != 0xff) {
		btn_calibrate();
	}
}
