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

using namespace hw;

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

hw::LedDisplay::~LedDisplay(void)
{
	this->force(0x00);
	this->write_backlight(0);
}

void hw::LedDisplay::init(doutCfg_t* dout_chs, uint8_t act_lvl, util::VCounter* timer, bsp::PwmOut* pwm_ch)
{
	this->led0_dout_ch = dout_chs->led0_dout_ch;
	this->led1_dout_ch = dout_chs->led1_dout_ch;
	this->led2_dout_ch = dout_chs->led2_dout_ch;
	this->led3_dout_ch = dout_chs->led3_dout_ch;
	this->led4_dout_ch = dout_chs->led4_dout_ch;
	this->led5_dout_ch = dout_chs->led5_dout_ch;
	
	if(act_lvl) this->act_lvl = 1;
	else this->act_lvl = 0;
	
	this->timer = timer;
	
	this->pwm_ch = pwm_ch;
	
	this->on_time = 0;
	this->period = 0;
	this->cycle_cnt = 0;
	this->cycle_limit = 0;
	this->timestamp_start = 0;
	
	this->force(0x00);
	this->write_backlight(0);
}

void hw::LedDisplay::force(uint8_t image)
{
	uint8_t led_state;
	
	if(image&0x01) led_state = 1;
	else led_state = 0;
	this->set_single_led(led_state, this->led0_dout_ch);
	
	if(image&0x02) led_state = 1;
	else led_state = 0;
	this->set_single_led(led_state, this->led1_dout_ch);
	
	if(image&0x04) led_state = 1;
	else led_state = 0;
	this->set_single_led(led_state, this->led2_dout_ch);
	
	if(image&0x08) led_state = 1;
	else led_state = 0;
	this->set_single_led(led_state, this->led3_dout_ch);
	
	if(image&0x10) led_state = 1;
	else led_state = 0;
	this->set_single_led(led_state, this->led4_dout_ch);
	
	if(image&0x20) led_state = 1;
	else led_state = 0;
	this->set_single_led(led_state, this->led5_dout_ch);
}

void hw::LedDisplay::write(uint8_t image)
{
	// Static mode
	this->on_time = 1;
	this->period = 0;
	this->cycle_cnt = 0;
	this->cycle_limit = 0;
	this->timestamp_start = 0;
	
	// Set initial state
	this->force(image);
}

void hw::LedDisplay::write(uint8_t image, uint16_t on_time, uint16_t period, uint8_t cycle_limit)
{
	// "PWM" mode
	this->on_time = on_time;
	this->period = period;
	this->cycle_cnt = 0;
	this->cycle_limit = cycle_limit;
	
	// Set initial state
	if(this->on_time > 0) this->force(image);
	else this->force(0x00);
	
	// Cycle start time
	this->timestamp_start = this->timer->read();
}

void hw::LedDisplay::process(void)
{
	if(this->period == 0) return; // Nothing to do
	
	// Update cycle timing
	uint16_t ts_now = this->timer->read();
	uint16_t td = util::time_delta(this->timestamp_start, ts_now);
	uint32_t td_ms = this->timer->convert_ms(td);
	
	if(td_ms >= this->period)
	{
		this->timestamp_start = ts_now;
		this->cycle_cnt++;
	};
	
	// Check cycle limit
	if((this->cycle_cnt >= this->cycle_limit)&&(this->cycle_limit))
	{
		this->on_time = 0;
		this->period = 0;
		this->timestamp_start = 0;
		this->force(0x00);
		return;
	};
	
	// Do output compare
	if(td_ms < this->on_time) this->force(this->image);
	else this->force(0x00);
}

uint8_t hw::LedDisplay::is_cycle_end(void)
{
	if(this->cycle_cnt >= this->cycle_limit) return 1;
	else return 0; 
}

void hw::LedDisplay::write_backlight(uint8_t percent)
{
	this->pwm_ch->write(percent);
}

void hw::LedDisplay::set_single_led(uint8_t state, bsp::DigitalOut* led_ch)
{
	uint8_t lvl = 0;
	
	if(((state==0)&&(this->act_lvl==0))||((state!=0)&&(this->act_lvl==1))) lvl = 1;
	
	led_ch->write(lvl);
}

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