#include "app.h"
#include <stdlib.h>

// #######################################################################################################
// ADC
// #######################################################################################################
static int adcChannel;
static uint adcIndex;
static int adcChange; // sinaliza para fazer a troca de canal no mux analógico
static u16 adcVal;
static int bufADC[ADC_MAX_CHANNEL][ADC_nSAMPLY_BUF];
static uint idxBufADC[ADC_MAX_CHANNEL];

static void adc_ClearBuf(int ch) {
	int y; for (y=0;y<ADC_nSAMPLY_BUF;y++) bufADC[ch][y]=-1;
	idxBufADC[ch] = 0;
}

static volatile uint timeReadAdc = 0;
static void analogic_Tick(void) {
	if (timeReadAdc) timeReadAdc--;
}

// FAZER CONVERSÃO DO CANAL ADC ATUAL, E QUANDO COMPLETADO PASSAR PARA O PRÓXIMO CANAL
static void analogic_Process(void) {
	// a velocidade de chamada da função escolheremos a velocidade de filtro para ruídos externos
	// logo faremos uma leitura mais lenta para filtrar ruídos de alta frequência
	// Fomos diminuido o tempo de amostragem e o tamanaho do buffer adc até que a estabilidade do display e na statisitica se mantém
	//	Usamos uma furadeira AC com escova e com fio do sensor enrolado nela para gerar os ruidos
	if (timeReadAdc) return;

	#if (ADC_MAX_CHANNEL == 2)
	timeReadAdc = 15; // O valor 5 está relacionado com o TEMP_INITREAD
	#elif (ADC_MAX_CHANNEL == 3)
	timeReadAdc = 10; // O valor 5 está relacionado com o TEMP_INITREAD
	#elif (ADC_MAX_CHANNEL == 4)
	timeReadAdc = 5; // O valor 5 está relacionado com o TEMP_INITREAD
	#else
	timeReadAdc = 20; // O valor 20 está relacionado com o TEMP_INITREAD e para 1 sensor
	#endif

	// checa se é para mudar de canal ADC e iniciar uma nova conversão
	if (adcChange) {
		adcChange = pdFALSE; // sinaliza para não fazer chaveamento do mux analógico

		if (adcIndex == ADC_MAX_CHANNEL-1)
				adcIndex = 0;
		else 	adcIndex++; // Apontar para o próximo canal

		// pega o canal ADC do uC
		if (adcIndex == 1) 		{ adcChannel = AD0_CHANNEL3;}
		//else if (adcIndex == 2) { adcChannel = AD0_CHANNEL4;}
		//else if (adcIndex == 3) { adcChannel = AD0_CHANNEL5;}
		else 					{ adcChannel = AD0_CHANNEL2;}

		adc0_Start(adcChannel, AD0_START_NOW, 0);

	// Checa se a conversão foi concluida
	} else if ( adc0_ReadNoWait(adcChannel, &adcVal) == pdPASS) {
		adcChange = pdTRUE; // sinaliza para trocar de canal analógico
		bufADC[adcIndex] [idxBufADC[adcIndex] % ADC_nSAMPLY_BUF] = adcVal;

		if (idxBufADC[adcIndex] > 0) {
			int qtd;
			if (idxBufADC[adcIndex] < ADC_nSAMPLY_BUF) qtd = idxBufADC[adcIndex]+1; else qtd = ADC_nSAMPLY_BUF;

			// media normal
			int m = 0;
			int i; for (i=0;i<qtd;i++) m += bufADC[adcIndex][i];
			m = m/qtd;

			// vamos remover o ponto discrepante
			int x;
			int k, d;
			k = idxBufADC[adcIndex] % qtd; //automaticamente considera o último ponto como sendo ruim
			d = 0; //nao tao ruim assim, por consideramos aqui com desvio "0" da media

			// procuramos pelo ponto mais longe
			for (i=0;i<qtd;i++) {
				x = abs(m - bufADC[adcIndex][i]); 	// qual a diferença deste ponto para media?
				if (x<d) continue;        					// é menor que o outro? entao esquece e vai para o proximo
				k = i;										// marca o ponto "i" como sendo o pior de todos ate agora
				d = x;										// anota quao ruim ele é
			}

			// vamor calcular novamente a media sem o ponto ruim
			m = 0;
			for (i=0;i<qtd;i++) if (i!=k) m += bufADC[adcIndex][i];
			m = m/(qtd-1); // -1 porque sabemos que foi retirado apenas 1 ponto

			idxBufADC[adcIndex]++;
			control.adc.value[adcIndex] = m;
		} else {
			idxBufADC[adcIndex]++;
			control.adc.value[adcIndex] = adcVal;
		}
	}
}

void adc_Init(void) {
	// Iniciar ADC com clock de trabalho. Deve ser multiplo inteiro de PCLK, ou seja, PCLK/adc_clk = valor inteiro
	adc0_Init(12500000, 0); // 12500000/5000000/500000/250000

	// inicaliza a conversão adc pelo primeiro canal
	adcIndex = ADC_MAX_CHANNEL-1;
	adcChange = pdTRUE; // sinaliza para fazer o mux de canal analógico

	// limpa as var que armazena valores adc
	int ch; for (ch=0;ch<ADC_MAX_CHANNEL;ch++)	{
		control.adc.value[ch] = -1;
		adc_ClearBuf(ch);
	}
}

int adc_Read(int ch) {
	if (ch == 2) ch = 0;
	else if (ch == 3) ch = 1;
	else return errDEVICE_NOT_FOUND;

	return control.adc.value[ch];
}

// #######################################################################################################
// KEYBOARD
// #######################################################################################################
void onKeyDown(char key) {
	lprintf("onKeyDown key [0x%x]"CMD_TERMINATOR, key);
	if (key == SW0) beep_Play(4);
	else if (key == SW1) beep_Play(5);
	else if (key == SW2) beep_Play(6);
}

void onKeyPress(char key __attribute__ ((unused)) ) {
	lprintf("onKeyPress key [0x%x] fastInc[%d] "CMD_TERMINATOR, key, gkey_FastInc);
}

void onKeyUp(char key __attribute__ ((unused)) ) {
	lprintf("onKeyUp key [0x%x] fastInc[%d] "CMD_TERMINATOR, key, gkey_FastInc);
}

// #######################################################################################################
// RELAYS
// #######################################################################################################
void relay_Init(void) {
	relay1_Init();
	relay2_Init();

	control.relay1.st = 0;
	control.relay1.timeout = 0;

	control.relay2.st = 0;
	control.relay2.timeout = 0;
}

// Liga ou desliga o rele
//	parametro rly: sinaliza qual o rele vamos acessar. valor 0 ou 1
//	parametro st: pdON liga o rele e pdOFF desliga o rele;
//	parametro timeout: O tempo em segundos que o rele vai ficar no estado atual e voltar para o estado inverso
//		Se colocar valor 0, permanece no estado atual até o próximo comando
int relay_Set(int rly, int st, uint timeout) {
	pRelay rl;

	if ( (rly < 0) || (rly > 1) ) return errDEVICE_NOT_FOUND;
	if (rly == 0) rl = &control.relay1; else rl = &control.relay2;

	rl->timeout = timeout*1000;
	rl->st = st;

	return pdPASS;
}

void relay_Tick(void) {
	if (control.relay1.st) relay1_On(); else relay1_Off();
	if (control.relay2.st) relay2_On(); else relay2_Off();
	if (control.relay1.timeout > 0) control.relay1.timeout--;
	if (control.relay2.timeout > 0) control.relay2.timeout--;
	//colocar depois do acionamento para que seja certinho a contagem dos tempos
	if (control.relay1.timeout == 1) control.relay1.st ^= 1;
	if (control.relay2.timeout == 1) control.relay2.st ^= 1;
}

// #######################################################################################################
// APPLICATION
// #######################################################################################################
void app_Init(void) {
	control.ticks = 0;
	control.key = 0;

	led1_Init();
	led2_Init();
	led3_Init();
	led4_Init();
	led5_Init();
	led6_Init();

	relay_Init();
}

// Aqui descre, ou chame, códigos que controlam tempos ou que sejam executados em tempos reguladores
void app_Tick(void) {
	// ATENÇÃO:  tempo de execução de TODOS os TICKS dentro dessa função NÃO pode ser superior a 1ms
	ledError_Tick();
	beep_Tick();
	gKey_Tick();
	relay_Tick();
	analogic_Tick();

	// LEITURA DO TECLADO a cada 3ms
	static volatile uint timeReadKey = 0;
	static int bufKey[3];
	static uint idx = 0;
	if (timeReadKey) timeReadKey--;
	if (timeReadKey == 0) {
		timeReadKey = 3;

		// fazer debounce no teclado
		bufKey[idx%3] = (sw0_PIN&sw0_NUMPIN)?SW0:0;
		bufKey[idx%3] |= (sw1_PIN&sw1_NUMPIN)?SW1:0;
		bufKey[idx%3] |= (sw2_PIN&sw2_NUMPIN)?SW2:0;

		if ( (bufKey[0] == bufKey[1]) && (bufKey[1] == bufKey[2]) ) {
			control.key = bufKey[2]&0x7; // para que não pegar o botão de reconfigure
		}
		if (idx == 2) idx = 0; else idx++;
	}

	control.ticks++; // Contador de ticks do sistema
}


// Aqui descre, ou chame, códigos que não controlam tempos ou que não sejam executados em tempo reguladores, pelo menos diretamente
void app_Process(void) {
	analogic_Process();

	static uint ledpos = 1;
	static uint ticks_d = 0;
	if ( (control.ticks%1000==0) && (control.ticks != ticks_d) ) {
		ticks_d = control.ticks;
		if (ledpos==0x01) led1_On(); else led1_Off();
		if (ledpos==0x02) led2_On(); else led2_Off();
		if (ledpos==0x04) led3_On(); else led3_Off();
		if (ledpos==0x08) led4_On(); else led4_Off();
		if (ledpos==0x10) led5_On(); else led5_Off();
		if (ledpos==0x20) led6_On(); else led6_Off();
		if (ledpos<0x20) ledpos<<=1; else ledpos=1;

		lcd_SetCursor(0, 0);
		lcd_printf("AD2:%d", control.adc.value[0]);
		lcd_SetCursor(0, 1);
		lcd_printf("AD3:%d", control.adc.value[1]);
	}
}
