/*
 * userio.c: reacting when you fiddle with buttons, switches, and knobs
 *
 * mode switch: modes are changed only when the button is not being
 * pushed, as button being pushed negates the mode switch response.
 * for the mode to change, the mode switch must be in the new position
 * without wavering for at least 3 rounds. this will prevent the
 * mode from changing by someone fucking around with the button or
 * when the button is used normally.
 * 
 * button: is connected to mode switch ADC. if below threshold, then
 * we can be certain button is pushed. we still wait a debounce
 * round to ensure button is pushed. our code can fire events on
 * either button being held or button being released. the actual
 * push event does not do anything with this code.
 * 
 * settings knobs: these are used by the settings module which works
 * with the other modules and the button for setting user settings.
 * the values are scaled from the ADC value, with bounds being set
 * on the low and high ends and further scaled for usability.
 * please read the manual if you want to know what settings do.
 *
 * file creation: 20231015 0122
 */


#include <stdint.h>
#include <stdlib.h>

#include "testo.h"
#include "userio.h"

#include "adc.h"
#include "probe.h"



#define MODE_SET_LIMIT		60      // 3x worst case expected (1% tol)
#define MODE_CHANGE_TICKS   4       // how many samples before switching modes

#define MODE_ANALOG_MIN     20


#ifdef TESTO_REV1
#define SET1_MAX			2047	// 3V net feeding into 1V24 via body diode above this
#else
#define SET1_MAX			1539	// 1.24Vref
#endif



static uint8_t mode = 0xff;
static uint8_t mode_next;
static uint8_t mode_count;
static const int16_t mode_targets[] = {
    MODE_CONT_TARGET, MODE_FUN_TARGET, MODE_DIODE_TARGET
};

uint8_t knob[2];

static uint8_t  btn = 0;
static uint16_t btn_held = 0;



void userio_parse()
{
    uint8_t i;
    int16_t m, w;
	uint32_t x;
    
    // button
	{
		// debounce is handled by the fact that we've had to have averaged
		// about 16ms of measured hold time below the zero threshold.
		m = adc_avg[ADC_SET_MODE];
		if (m < MODE_ANALOG_MIN) {
			// button is pushed
			btn = 1;
			if (btn_held != 0xffff) btn_held++;
		} else if (btn == 1) {
			// button is released
			btn = 0;
		}
	}

    // mode
	if (!btn) {
		for (i = 0; i < 4; i++) {
			// clear mode_count if we aren't actively changing modes
			if (i == 3) {
				mode_count = 0;
				break;
			}

			// normalize mode selection
			w = abs(m - mode_targets[i]);

			// determine if we mode switch
			if (w < MODE_SET_LIMIT) {
				// currently in this mode? if so, ignore it
				if (mode == i) continue;

				// is the next mode set to this item?
				if (mode_next == i) {
					// increment the verification count
					mode_count++;
					if (mode_count >= MODE_CHANGE_TICKS) {
						// we've got enough data to do a change of mode
						mode = i;
						mode_count = 0;

						// MODE SWITCH ACTION IS HANDLED HERE
						// restart ADC on mode change to update values
						// to reflect selected measurement mode
						adc_switch0();
						// and set the probe measurement routines
						// to use the selected mode
						probe_mode_switch(mode);
					}
				} else {
					// set the next mode to the newly selected mode
					mode_next = i;
					mode_count = 0;
				}

				break;
			}
		}
	}

	// knobs
	{
		// SET0 is always connected, so just copy and scale it
		knob[0] = adc_avg[ADC_SET0] >> 4;
	
		// SET1/VREFEXT input can be SET1 or VREFEXT, depending on PROBESEL.
		// only copy if not set to VREFEXT, and only if within valid range
		if (PROBESEL_PORT->ODR & (1 << PROBESEL_PIN)) {
			// PROBESEL is high, so SET1 is in use
			if (adc_avg[ADC_SET1] > SET1_MAX) {
				// knob is turned too far; warn the user.
				// TODO
			} else {
				x  = adc_avg[ADC_SET1] << 12;	// maximum possible level
				x /= SET1_MAX;					// normalize to 4096max
				knob[1] = (x >> 4) & 0xff;		// reduce to 8-bit
			}
		}
	}
}

uint8_t userio_get_mode()
{
	return mode;
}

/*
 * returns the button state.
 * -1 = button is currently being pressed
 *  0 = button is not being pressed
 * >0 = button is not being pressed, but was (just?) released and held for X ticks
 */
int16_t userio_get_btn()
{
	int16_t ret;

	if (btn) return -1;
	if (!btn) {
		if (btn_held) {
			ret = btn_held;
			btn_held = 0;
			return ret;
		}
	}

	return 0;
}