/*
 * $Id: btn.c 452 2019-06-14 00:11:51Z true $
 * begin 20190527 true
 */

#include "ui/btn.h"

#include "user_config.h"


BtnData xdata btn[BTN_COUNT];


void btn_init()
{
	uint8_t i;

	for (i = 0; i < BTN_COUNT; i++) {
		btn[i].state = 0;
		btn[i].held = 0;
		btn[i].hold_time = 0;
		btn[i].push_cb = 0;
		btn[i].held_cb = 0;
		btn[i].release_cb = 0;
	}

	// the rest should be set by configurator, but let's make sure.

	// configure as digital inputs
	BTN1_DIN |= (1 << BTN1_PIN);
	BTN2_DIN |= (1 << BTN2_PIN);
	BTN3_DIN |= (1 << BTN3_PIN);
	BTN4_DIN |= (1 << BTN4_PIN);

	// as open drain
	BTN1_DOUT &= ~(1 << BTN1_PIN);
	BTN2_DOUT &= ~(1 << BTN2_PIN);
	BTN3_DOUT &= ~(1 << BTN3_PIN);
	BTN4_DOUT &= ~(1 << BTN4_PIN);

	// and don't drive low; make sure p/u is enabled
	BTN1 = 1;
	BTN2 = 1;
	BTN3 = 1;
	BTN4 = 1;
}

static void btn_check(uint8_t idx, uint8_t state)
{
	if (sysflags & SYS_OLED_ROTATE_X) {
		idx += 2;
		idx %= 4;
	}

	if (!state) {	// buttons active low
		if (btn[idx].held < BTN_MAX_HOLD) {
			btn[idx].held++;
		}
	} else {
		btn[idx].held = 0;
	}
}

void btn_tick()
{
	uint8_t i;

	// increment hold
	btn_check(0, BTN1);
	btn_check(1, BTN2);
	btn_check(2, BTN3);
	btn_check(3, BTN4);

	// process
	for (i = 0; i < BTN_COUNT; i++) {
		// test for hold / release, can only happen after push
		if ((btn[i].state & BTN_PUSH) || (btn[i].state & BTN_HELD)) {
			// release first
			if (btn[i].held == 0) {
				// yes, released
				btn[i].state = BTN_RELEASE;

			// do we trigger hold?
			} else if (btn[i].hold_time && (btn[i].held >> BTN_HOLD_SHIFT) == btn[i].hold_time) {
				// yes, held, but only set if we didn't already
				if (!(btn[i].state & BTN_HELD)) {
					btn[i].state = BTN_HELD;
					btn[i].hold_rt_ctr = btn[i].hold_retrig;
				}
			}
		} else {
			// push
			if (btn[i].held == BTN_DEBOUNCE) {
				btn[i].state = BTN_PUSH;
			}
		}
	}
}

void btn_callback()
{
	uint8_t i;
	uint8_t s;

	for (i = 0; i < BTN_COUNT; i++) {
		s = btn[i].state;

		if ((s & BTN_PUSH) && !(s & BTN_PUSH_CB)) {
			btn[i].state |= BTN_PUSH_CB;
			if (btn[i].push_cb) {
				btn[i].push_cb();
			}
		}

		if (btn[i].hold_retrig) {
			if (btn[i].hold_rt_ctr) {
				btn[i].hold_rt_ctr--;
			} else {
				btn[i].hold_rt_ctr = btn[i].hold_retrig;
				btn[i].state &= ~(BTN_HELD_CB);
			}
		}

		if ((s & BTN_HELD) && !(s & BTN_HELD_CB)) {
			btn[i].state |= BTN_HELD_CB;
			if (btn[i].held_cb) {
				btn[i].held_cb();
			}
		}

		if ((s & BTN_RELEASE) && !(s & BTN_RELEASE_CB)) {
			btn[i].state |= BTN_RELEASE_CB;
			if (btn[i].release_cb) {
				btn[i].release_cb();
			}
		}
	}
}
