#ifndef SOLDERDOODLE_HEAT_H
#define SOLDERDOODLE_HEAT_H

#include <stdbool.h>
#include <stdint.h>
#include "const.h"

// Same number of heat levels as LEDs
#define HEAT_LEVEL_COUNT      (LED_COUNT)

typedef enum {
	k_heat_unknown = 0,
	k_heat_valid_result = 1,
	k_heat_on = 2,
	k_heat_did_change = 4,
	k_heat_touch_ok = 8
} HeatTickStatus;

typedef struct {
	int16_t on;	// in milliseconds
	int16_t off;
} DutyCycle;

const DutyCycle HEAT_DUTY_CYCLES[LED_COUNT] = {
	{.on =    0, .off = 1000},	// off
	{.on = 1000, .off = 3000},	// ?
	{.on = 1000, .off = 2000},	// Removing 3D Print Spider Webs Without Melting Part?
	{.on = 1000, .off = 1000},	// Leaded Solder small pads
	{.on = 2000, .off = 1000},	// Lead-Free small Solder pads/Leaded Solder on Ground Plane
	{.on = 3000, .off = 1000},
	{.on = 5000, .off = 1000}	// Lead-Free ground planes / 20ga wire
};

// Touch window: for HEAT_ON_FIRM_TOUCH.
// Only allow changes to the heat state at this interval:
const int16_t HEAT_TOUCH_WINDOW_INTERVAL = 1000;

int16_t phase_time = 0;

// is_heat_enabled: Kill-switch to disable heat entirely.
bool is_heat_enabled = false;

bool is_on = true;	// Is heat currently on?

bool is_duty_cycle_on = false;

// HEAT_ON_FIRM_TOUCH: Is the user pressing hard enough?
bool is_heat_touch_ok = false;

#if HEAT_ON_FIRM_TOUCH
int16_t heat_touch_window_time = 0;
int16_t heat_touch_max = 0;
#endif

void heat_reset_touch_window()
{
#	if HEAT_ON_FIRM_TOUCH
		heat_touch_window_time = 0;
		heat_touch_max = INT16_MIN;
#	endif
}

void heat_init()
{
	pinMode(HEAT_ENABLE, OUTPUT);
	digitalWrite(HEAT_ENABLE, LOW);	// On boot: No heat!

	heat_reset_touch_window();
}

HeatTickStatus heat_tick(uint8_t elapsed, int8_t heatLevel, int16_t touchPressure)
{
	HeatTickStatus status = k_heat_valid_result;

	// Sanity check heatLevel value
	if (is_heat_enabled) {
		heatLevel = constrain(heatLevel, 0, HEAT_LEVEL_COUNT - 1);

	} else {
		// is_heat_enabled false: Kill switch.
		heatLevel = 0;
	}

	//
	//  DUTY CYCLE
	//

	// Get the correct DutyCycle for the heatLevel
	const DutyCycle * cycle = &HEAT_DUTY_CYCLES[heatLevel];

	// How long (in milliseconds) is this phase?
	int16_t duty_time = (is_on ? cycle->on : cycle->off);

	// Move to the next phase?
	phase_time += elapsed;

	if (phase_time >= duty_time) {
		phase_time -= duty_time;

		// Force the touch window to be evaluated.
#		if HEAT_ON_FIRM_TOUCH
			heat_touch_window_time = HEAT_TOUCH_WINDOW_INTERVAL;
#		endif

		int16_t next_duty_time = (is_duty_cycle_on ? cycle->off : cycle->on);

		// Only change phase if the next phase time
		// is greater than zero.
		if (next_duty_time > 0) {
			// Change phase (on<=>off)
			is_duty_cycle_on = !is_duty_cycle_on;
		}
	}

	//
	//  TOUCH ACTIVATION
	//

#	if HEAT_ALL_THE_TIME
		is_heat_touch_ok = true;	// Always allow heat

# elif HEAT_ON_FIRM_TOUCH
		heat_touch_max = max(heat_touch_max, touchPressure);

		heat_touch_window_time += elapsed;

		// Every 1000 milliseconds: Update heat-touch.
		if (heat_touch_window_time >= HEAT_TOUCH_WINDOW_INTERVAL) {

			// Based on min&max touch values: Can we
			// latch the heat on/off?
			if (heat_touch_max >= HEAT_PRESS_LATCH_ON) {
				is_heat_touch_ok = true;

			} else if (heat_touch_max < HEAT_PRESS_LATCH_OFF) {
				is_heat_touch_ok = false;
			}

			heat_reset_touch_window();
		}

#	endif

	//
	//  UPDATE THE PIN
	//

	bool old_is_on = is_on;

	// New pin state:
	is_on = (is_duty_cycle_on && is_heat_touch_ok);

	// If is_on changed, update the pin.
	if (old_is_on != is_on) {
		digitalWrite(HEAT_ENABLE, is_on ? HIGH : LOW);

		status |= k_heat_did_change;
	}

	if (is_on) {
		status |= k_heat_on;
	}

	if (touchPressure >= HEAT_PRESS_LATCH_OFF) {
		status |= k_heat_touch_ok;
	}

	return status;
}

void heat_enable()
{
	is_heat_enabled = true;
}

void heat_disable()
{
	is_heat_enabled = false;
	heat_tick(0, 0, 0);
}

#endif
