/**** Includes ****/
#include "../utils/utils.h"
#include "button.h"

using namespace hw;

/**** Private definitions ****/
/**** Private constants ****/
/**** Private variables ****/
/**** Private function declarations ****/
/**** Public function definitions ****/
hw::Button::Button(void)
{
	return;
}

hw::Button::~Button(void)
{
	return;
}

void hw::Button::init(bsp::DigitalIn* din_ch, uint8_t act_lvl, util::VCounter* timer, uint16_t dbnc_lim)
{
	this->din_ch = din_ch;
	this->timer = timer;
	
	if(act_lvl) this->act_lvl = 1;
	else this->act_lvl = 0;
	
	this->state_start_ts = 0;
	this->dbnc_ts = 0;
	this->dbnc_lim = dbnc_lim;
	
	this->state = BUTTON_OFF;
	this->is_new = 0;
}

uint8_t hw::Button::process(void)
{
	// Read din
	if(this->update_din) this->din_ch->read();
	
	// Get last read level
	uint8_t lvl = this->din_ch->last_read;
	
	// Determine next state
	uint8_t next_state = BUTTON_OFF;
	if(lvl==this->act_lvl) next_state = BUTTON_ON;
	
	// Advance debounce sample counter
	uint16_t ts_now = this->timer->read();
	
	if(next_state != this->state)
	{
		if(this->dbnc_ts == 0) this->dbnc_ts = ts_now;
		uint16_t td = util::time_delta(this->dbnc_ts, ts_now);
		uint32_t td_ms = this->timer->convert_ms(td);
		// Check for debounce end
		if(td_ms >= this->dbnc_lim) 
		{
			// Debounce end. Apply new state.
			this->dbnc_ts = 0;
			this->state = next_state;
			this->state_start_ts = ts_now;
			this->is_new = 1;
		};
	}
	else this->dbnc_ts = 0;

	return this->state;
}

uint8_t hw::Button::force_read(void)
{
	// Read din
	if(this->update_din) this->din_ch->read();
	
	// Get last read level
	uint8_t lvl = this->din_ch->last_read;
	
	// Cancels active debounce
	this->dbnc_ts = 0;
	
	// Determine next state
	uint8_t next_state = BUTTON_OFF;
	if(lvl==this->act_lvl) next_state = BUTTON_ON;
	
	if(next_state != this->state)
	{
		this->state_start_ts = this->timer->read();
		this->state = next_state;
		this->is_new = 1;
	};
	
	return this->state;
}

uint32_t hw::Button::time_read(void)
{
	uint16_t ts_now = this->timer->read();
	uint16_t td = util::time_delta(this->state_start_ts, ts_now);
	return this->timer->convert_ms(td);
}

void hw::Button::time_reset(void)
{
	this->state_start_ts = this->timer->read();
}

uint32_t hw::Button::time_read_max(void)
{
	uint16_t ts_max = this->timer->read_top();
	return this->timer->convert_ms(ts_max);
}

/**** Private function definitions ****/
