#include "main.h"
#include "tim.h"
#include "ctrl_bottom_button.h"
#include "ctrl_channel_button.h"
#include "ctrl_encoder.h"
#include "ctrl_disp_button.h"
#include "ctrl_signal_gen.h"

#include "ctrl_app.h"

typedef void (*btn_action_t)(APP_data_t *app_data);

typedef struct
{
	btn_action_t command;
	BITMAP_buttonName_t bitmap_name;
} CMD_button_t;

static const CMD_button_t btn_command[BTN_STATE_MAX][DISP_BTN_MAX];
static const LAY_dispBtn_t btn_hw_to_disp[BTN_BOT_MAX] = {DISP_BTN_1, DISP_BTN_2, DISP_BTN_3, DISP_BTN_4, DISP_BTN_5};
static const GEN_channel_t btn_ch_to_chan[CHANNEL_MAX] = {CHANNEL1, CHANNEL2, CHANNEL3, CHANNEL4, CHANNEL5, CHANNEL6};
static const uint32_t pow_of_10[7] = {1, 10, 100, 1000, 10000, 100000, 1000000};

static APP_data_t *_app_data;

static dds_gen_t dds_gen[DDS_CH_MAX] = {
	{.super.type = GEN_FG_TYPE, .dds_ch = DDS_CH1},
	{.super.type = GEN_FG_TYPE, .dds_ch = DDS_CH2},
	{.super.type = GEN_FG_TYPE, .dds_ch = DDS_CH3},
};
static pwm_gen_t pwm_gen[PWM_CH_MAX] = {
	{.super.type = GEN_PWM_TYPE, .pwm_ch = PWM_CH1},
	{.super.type = GEN_PWM_TYPE, .pwm_ch = PWM_CH2},
	{.super.type = GEN_PWM_TYPE, .pwm_ch = PWM_CH3},
};

static signal_gen_t *signal_gen[CHANNEL_MAX] = {
	&dds_gen[DDS_CH1].super,
	&dds_gen[DDS_CH2].super,
	&dds_gen[DDS_CH3].super,
	&pwm_gen[PWM_CH1].super,
	&pwm_gen[PWM_CH2].super,
	&pwm_gen[PWM_CH3].super,
};

static void _setGenInitailState(GEN_channel_t channel);
static void _signalGenDefaultValues(void);
static void _changeValueDdsGen(int8_t dir);
static void _changeValuePwmGen(int8_t dir);

//****************************
// 		Initialization
//****************************

void CTRL_buttonsInit(void)
{
	CTRL_bottomButtonInit();
	CTRL_channelButtonInit();
}

void APP_init(APP_data_t *app_data)
{
	_app_data = app_data;
	_app_data->freq_focus_digit = 4;
	_app_data->ampl_focus_digit = 1;
	_app_data->offs_focus_digit = 1;
	_app_data->phas_focus_digit = 0;
	_app_data->duty_focus_digit = 0;

	_app_data->curr_gen = signal_gen[CHANNEL1];

	_app_data->curr_state_lay = LAY_MAIN;
	_app_data->curr_state_btn = BTN_MAIN_FG;
	_app_data->curr_channel = CHANNEL1;

	// _app_data->isChannelChange = 1;
	// _app_data->isGraphChange = 1;
	// _app_data->isValueChange = 1;
	// _app_data->isButtonChange = 1;
	// _app_data->isButtonBlink = 1;
	_app_data->disp_update |= UPDATE_BUTTON | UPDATE_GRAPH | UPDATE_VALUE | UPDATE_CHANNEL;

	CTRL_buttonsInit();
	_signalGenDefaultValues();
	GEN_init(signal_gen);
	_setGenInitailState(CHANNEL1);
	_setGenInitailState(CHANNEL2);
}

//****************************
// 		   Handlers
//****************************

void CTRL_buttonsHandler(void)
{
	CTRL_bottomButtonsHandler();
	CTRL_channelButtonsHandler();
	CTRL_encoderHandler();
}

//****************************
// 		    Events
//****************************

void CTRL_pushedDispBtnEvent(ButtonKey_t *key)
{
	ULOG_TRACE("Disp btn: %d", key->instance);
	_app_data->last_key = btn_hw_to_disp[key->instance];
	btn_command[_app_data->curr_state_btn][_app_data->last_key].command(_app_data);
}

void CTRL_encoderEvent(int8_t enc)
{
	ULOG_TRACE("Enco event: %i", enc);
	switch (_app_data->curr_gen->type)
	{
	case GEN_FG_TYPE:
		_changeValueDdsGen(enc / 2);
		break;
	case GEN_PWM_TYPE:
		_changeValuePwmGen(enc / 2);
		break;

	default:
		ULOG_ERROR("%s:%d: Unknown generator type: %d", __FILE__, __LINE__, _app_data->curr_gen->type);
		break;
	}
}

void CTRL_pushedChanBtnEvent(ButtonKey_t *key)
{
	ULOG_TRACE("Chan btn: %d", key->instance);
	GEN_channel_t channel = btn_ch_to_chan[key->instance];

	_app_data->curr_gen = signal_gen[channel];

	_app_data->curr_state_lay = LAY_MAIN;
	_app_data->curr_state_btn = BTN_MAIN_FG;
	_app_data->curr_channel = channel;

	// _app_data->isChannelChange = 1;
	// _app_data->isGraphChange = 1;
	// _app_data->isValueChange = 1;
	// _app_data->isButtonChange = 1;
	// _app_data->isButtonBlink = 1;
	_app_data->disp_update |= UPDATE_BUTTON | UPDATE_GRAPH | UPDATE_VALUE | UPDATE_CHANNEL;
}

void CTRL_longPushedChanBtnEvent(ButtonKey_t *key)
{
	ULOG_TRACE("Chan btn(long): %d", key->instance);
	GEN_channel_t channel = btn_ch_to_chan[key->instance];

	switch (signal_gen[channel]->type)
	{
	case GEN_FG_TYPE:
	{
		dds_gen_t *dds_gen = (dds_gen_t *)signal_gen[channel];
		dds_gen->settings.enabled = dds_gen->settings.enabled ? FALSE : TRUE;
		setEnabled(signal_gen[channel]);
		break;
	}
	case GEN_PWM_TYPE:
	{
		pwm_gen_t *pwm_gen = (pwm_gen_t *)signal_gen[channel];
		pwm_gen->settings.enabled = pwm_gen->settings.enabled ? FALSE : TRUE;
		setEnabled(signal_gen[channel]);
		break;
	}
	default:
		ULOG_ERROR("%s:%d: Unknown generator type: %d", __FILE__, __LINE__, _app_data->curr_gen->type);
		break;
	}
}

//*******************************************************************

static void _changeValueDdsGen(int8_t dir)
{
	dds_gen_t *dds_gen = (dds_gen_t *)_app_data->curr_gen;

	switch (_app_data->curr_state_lay)
	{
	case LAY_FREQ:
	{
		uint32_t new_freq = dir * pow_of_10[_app_data->freq_focus_digit] + dds_gen->settings.frequency;
		ULOG_DEBUG("<FG> New freq: %lu", new_freq);
		if (new_freq > MAX_FREQ)
		{
			return;
		}
		dds_gen->settings.frequency = new_freq;
		setFrequency(&dds_gen->super);
		break;
	}
	case LAY_AMPL:
	{
		uint16_t new_ampl = dir * pow_of_10[_app_data->ampl_focus_digit] + dds_gen->settings.amplitude;
		ULOG_DEBUG("<FG> New ampl: %u", new_ampl);
		if (dds_gen->settings.offset + new_ampl > MAX_VOLT_POS || dds_gen->settings.offset - new_ampl < MAX_VOLT_NEG)
		{
			return;
		}
		dds_gen->settings.amplitude = new_ampl;
		setAmplitude(&dds_gen->super);
		break;
	}
	case LAY_OFFS:
	{
		int16_t new_offs = dir * pow_of_10[_app_data->offs_focus_digit] + dds_gen->settings.offset;
		ULOG_DEBUG("<FG> New offs: %i", new_offs);
		if (new_offs + dds_gen->settings.amplitude > MAX_VOLT_POS || new_offs - dds_gen->settings.amplitude < MAX_VOLT_NEG)
		{
			return;
		}
		dds_gen->settings.offset = new_offs;
		setOfsset(&dds_gen->super);
		break;
	}
	case LAY_PHAS:
	{
		uint16_t new_phas = dir * pow_of_10[_app_data->phas_focus_digit] + dds_gen->settings.phase;
		ULOG_DEBUG("<FG> New phas: %u", new_phas);
		if (new_phas > MAX_PHAS)
		{
			return;
		}
		dds_gen->settings.phase = new_phas;
		setPhase(&dds_gen->super);
		break;
	}
	default:
		ULOG_ERROR("%s:%d: Unknown layout: %d", __FILE__, __LINE__, _app_data->curr_state_lay);
		return;
	}

	// _app_data->isValueChange = 1;
	// _app_data->isGraphChange = 1;
	_app_data->disp_update |= UPDATE_GRAPH | UPDATE_VALUE;
}

static void _changeValuePwmGen(int8_t dir)
{
	pwm_gen_t *pwm_gen = (pwm_gen_t *)_app_data->curr_gen;

	switch (_app_data->curr_state_lay)
	{
	case LAY_FREQ:
	{
		uint32_t new_freq = dir * pow_of_10[_app_data->freq_focus_digit] + pwm_gen->settings.frequency;
		ULOG_DEBUG("<PWM> New freq: %lu", new_freq);
		if (new_freq > MAX_FREQ)
		{
			return;
		}
		pwm_gen->settings.frequency = new_freq;

		break;
	}
	case LAY_AMPL:
	{
		uint16_t new_ampl = dir * pow_of_10[_app_data->ampl_focus_digit] + pwm_gen->settings.amplitude;
		ULOG_DEBUG("<PWM> New ampl: %u", new_ampl);
		if (pwm_gen->settings.offset + new_ampl > MAX_VOLT_POS || pwm_gen->settings.offset - new_ampl < MAX_VOLT_NEG)
		{
			return;
		}
		pwm_gen->settings.amplitude = new_ampl;

		break;
	}
	case LAY_OFFS:
	{
		int16_t new_offs = dir * pow_of_10[_app_data->offs_focus_digit] + pwm_gen->settings.offset;
		ULOG_DEBUG("<PWM> New offs: %i", new_offs);
		if (new_offs + pwm_gen->settings.amplitude > MAX_VOLT_POS || new_offs - pwm_gen->settings.amplitude < MAX_VOLT_NEG)
		{
			return;
		}
		pwm_gen->settings.offset = new_offs;

		break;
	}
	case LAY_PHAS:
	{
		uint16_t new_phas = dir * pow_of_10[_app_data->phas_focus_digit] + pwm_gen->settings.phase;
		ULOG_DEBUG("<PWM> New phas: %u", new_phas);
		if (new_phas > MAX_PHAS)
		{
			return;
		}
		pwm_gen->settings.phase = new_phas;
		break;
	}
	case LAY_DUTY:
	{
		uint8_t new_duty = dir * pow_of_10[_app_data->duty_focus_digit] + pwm_gen->settings.duty;
		ULOG_DEBUG("<PWM> New duty: %u", new_duty);
		if (new_duty > MAX_DUTY)
		{
			return;
		}
		pwm_gen->settings.duty = new_duty;
		break;
	}
	default:
		ULOG_ERROR("%s:%d: Unknown layout: %d", __FILE__, __LINE__, _app_data->curr_state_lay);
		return;
	}

	// _app_data->isValueChange = 1;
	// _app_data->isGraphChange = 1;
	_app_data->disp_update |= UPDATE_GRAPH | UPDATE_VALUE;
}

void _signalGenDefaultValues(void)
{
	for (GEN_channel_t channel = CHANNEL1; channel < CHANNEL_MAX; channel++)
	{
		switch (signal_gen[channel]->type)
		{
		case GEN_FG_TYPE:
		{
			dds_gen_t *dds_gen = (dds_gen_t *)signal_gen[channel];
			dds_gen->settings.frequency = 1000;
			dds_gen->settings.amplitude = 100;
			dds_gen->settings.offset = 165;
			dds_gen->settings.phase = 0;
			dds_gen->settings.wave = GEN_SIN;
			dds_gen->settings.link = 0;
			dds_gen->settings.enabled = FALSE;
			break;
		}
		case GEN_PWM_TYPE:
		{
			pwm_gen_t *pwm_gen = (pwm_gen_t *)signal_gen[channel];
			pwm_gen->settings.frequency = 1000;
			pwm_gen->settings.amplitude = 100;
			pwm_gen->settings.offset = 0;
			pwm_gen->settings.phase = 0;
			pwm_gen->settings.duty = 50;
			pwm_gen->settings.link = 0;
			pwm_gen->settings.enabled = FALSE;
			break;
		}
		default:
			break;
		}
	}
}

void _setGenInitailState(GEN_channel_t channel)
{
	setFrequency(signal_gen[channel]);
	setWave(signal_gen[channel]);
	setPhase(signal_gen[channel]);
	setAmplitude(signal_gen[channel]);
	setEnabled(signal_gen[channel]);
	// setOfsset(signal_gen[channel]);
}

inline BITMAP_buttonName_t CTRL_getBitmapName(LAY_dispBtn_t disp_btn)
{
	return btn_command[_app_data->curr_state_btn][disp_btn].bitmap_name;
}

static const CMD_button_t btn_command[BTN_STATE_MAX][DISP_BTN_MAX] = {
	{
		// BTN_MAIN_FG
		{enterToFreqLayout, BITMAP_BTN_FREQ},
		{enterToAmplLayout, BITMAP_BTN_AMPL},
		{enterToOffslLayout, BITMAP_BTN_OFFS},
		{enterToPhasLayout, BITMAP_BTN_PHAS},
		{enterToWavelLayout, BITMAP_BTN_WAVE},
	},
	{
		// BTN_MAIN_PWM
		{enterToFreqLayout, BITMAP_BTN_FREQ},
		{enterToAmplLayout, BITMAP_BTN_AMPL},
		{enterToOffslLayout, BITMAP_BTN_OFFS},
		{enterToPhasLayout, BITMAP_BTN_PHAS},
		{enterToDutyLayout, BITMAP_BTN_NONE},
	},
	{
		// BTN_FREQ
		{moveToLeftFocusFreqNumber, BITMAP_BTN_LEFT},
		{moveToRighttFocusFreqNumber, BITMAP_BTN_RIGHT},
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},
	{
		// BTN_FREQ_MIN
		{moveToLeftFocusFreqNumber, BITMAP_BTN_LEFT},
		{doNoting, BITMAP_BTN_EMPTY},
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},
	{
		// BTN_FREQ_MAX
		{doNoting, BITMAP_BTN_EMPTY},
		{moveToRighttFocusFreqNumber, BITMAP_BTN_RIGHT},
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},
	{
		// BTN_AMPL
		{setTo1xFocusNumber, BITMAP_BTN_X1},
		{setTo0_1xFocusNumber, BITMAP_BTN_X0_1},
		{setTo0_01xFocusNumber, BITMAP_BTN_X0_01},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},
	{
		// BTN_OFFS
		{setTo1xFocusNumber, BITMAP_BTN_X1},
		{setTo0_1xFocusNumber, BITMAP_BTN_X0_1},
		{setTo0_01xFocusNumber, BITMAP_BTN_X0_01},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},
	{
		// BTN_PHAS
		{setTo10xFocusNumber, BITMAP_BTN_X10},
		{setTo1xFocusNumber, BITMAP_BTN_X1},
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},
	{
		// BTN_DUTY
		{setTo10xFocusNumber, BITMAP_BTN_X10},
		{setTo1xFocusNumber, BITMAP_BTN_X1},
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},
	{
		// BTN_WAVE
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{doNoting, BITMAP_BTN_NONE},
		{backToMain, BITMAP_BTN_BACK},
	},

};