/*
 * uart_echo.c
 *
 * Created: 16.08.2018 17:39:49
 * Author : jens
 */ 

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>

#include "fifo.h"
#include "message.h"
#include "adc.h"
#include "port.h"
#include "timer.h"
#include "uart.h"
#include "i2c.h"
#include "mcp42x6.h"

#define PRINT(a,...)	printf(a, ##__VA_ARGS__)
#define ARDUINO_NANO			1

#define PRINT_PROMPT			putchar(0x0D);putchar(0x0A);putchar(':');
#if ARDUINO_NANO
#define F_CPU			(16000000UL)	// MHz
#else
#define F_CPU			(18432000UL)	// MHz
#endif
#define USART_BAUDRATE	9600UL
#define BAUD_PRESCALE	(((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define TIMER_HW_CLOCK	(F_CPU/8)
#define TIMER_IRQ_CLOCK	1000UL
#define TIMER_HW_RELOAD	(0xFFFF-(TIMER_HW_CLOCK/TIMER_IRQ_CLOCK))

#define TIMER_SW_DELAY_MS(dly)	((dly*TIMER_IRQ_CLOCK)/1000UL)

uint8_t ledState = 0;
int16_t adc_last = 0;
int sw_in_last = 0;
size_t cmdStrSize = 0;
Fifo messageFifo;

FILE uart_file = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

typedef enum _eSwitch
{
	SwitchOff = 0,
	SwitchOn = 1	
} Switch;

void setSwitch(int on)
{
	const char *sw_str[2] = {"OFF", "ON"};
	PRINT("Set switch to %s\n", sw_str[on != 0]);
	portSet(PwrSwitch_out, on != 0);
}

typedef enum _ePower
{
	PowerLow = 4095,
	PowerMax = 0	
} Power;

void setPower(uint16_t power)
{
	PRINT("Set Power to %u\n", power);
	uint8_t dac_cmd[] = {0x00, 0x00};					// 6.2 Write Volatile Memory (C2:C0 = ‘010’)
	size_t res = MCP47x6_write_volatile_dac(power, dac_cmd, sizeof(dac_cmd));
	i2c_send(0x60, I2C_WRITE, dac_cmd, res);
}

typedef enum _eState_t
{
	StateNormal = 0,	
	StateRemote = 1,
	StateError = 2,
	State_NumStates
} State_t;

int main(void)
{
	cli();
    wdt_reset();
    wdt_disable();
		
	const char *state_str[State_NumStates] = {"Normal","Remote","Error"};
		
	State_t state = StateNormal;
	State_t state_next = StateNormal;
	
	fifo_init(&messageFifo, 16, sizeof(Msg_t), "Fifo");
	
    /* Replace with your application code */
	uart_init(&messageFifo, BAUD_PRESCALE);
	timer_init(&messageFifo, TIMER_HW_RELOAD, TimerClockSel_PS_8);
	adc_init(&messageFifo);
	i2c_init();
	port_init();
	
	stdout = &uart_file;
	printf("\nHendi Control v1.0-beta\n");

	uint8_t dac_cmd[] = {0x00, 0x00, 0x00};		// 6.1 Write Volatile DAC Register (C2:C0 = ‘00x’)
	size_t res = MCP47x6_write_volatile_mem(0x0000, dac_cmd, sizeof(dac_cmd));
	i2c_send(0x60, I2C_WRITE, dac_cmd, res);

	char cmdStr[16];
	PRINT_PROMPT;
	
	// Start Timer
	timer_start(TIMER_GENERAL, 0);
	timer_start(TIMER_ADC, 0);
	setSwitch(1);

	sei();
	while (1) 
    {
		state_next = state;
		Msg_t msg;
		if (!fifo_isEmpty(&messageFifo))
		{
			fifo_pop(&messageFifo, &msg);
			switch(msg.code)
			{
				case Uart:
				{
					// Process Uart message
					cmdStr[cmdStrSize] = 0;
					char c = msg.data;
						
					// Command finished with CR/LF
					if (c == 0x0A || c == 0x0D)
					{
						PRINT_PROMPT;
						if (cmdStrSize < 2)
						{
							break;
						}

						if (state == StateNormal || state == StateRemote)
						{
							// Power Control
							if (toupper(cmdStr[0]) == 'P')
							{
								uint16_t arg = (uint16_t)strtol(cmdStr+1, (char **)NULL, 10);
								setPower(arg);
								timer_start(TIMER_TIMEOUT, TIMER_SW_DELAY_MS(5000));
								state_next = StateRemote;
							}

							// Switch Control
							if (toupper(cmdStr[0]) == 'S')
							{
								uint16_t arg = (uint16_t)strtol(cmdStr+1, (char **)NULL, 10);
								setSwitch(arg);
								timer_start(TIMER_TIMEOUT, TIMER_SW_DELAY_MS(5000));
								state_next = StateRemote;
							}
						}
						cmdStrSize = 0;
					}
					else
					{
						if (cmdStrSize < (sizeof(cmdStr)-1))
						{
							cmdStr[cmdStrSize++] = c;
						}
						putchar(c);
					}
				}
				break;

				case AdcComplete:
				{
					int16_t adc_curr = adc_getData();
					if (adc_getChannel() == 6)
					{
						if (((adc_curr - adc_last) < -1) || ((adc_curr - adc_last) > 1))
						{
							if (state == StateNormal)
							{
								setPower(adc_curr << 2);
							}
						}
					}
					adc_last = adc_curr;
				}
				break;

				case Timer:
				{
					int timer_id = msg.data;
					switch(timer_id)
					{
						case TIMER_GENERAL:
						{
							timer_start(timer_id, TIMER_SW_DELAY_MS(500));
							switch(ledState)
							{
								case 0:
								PORTB = (PORTB & 0xFC) | 0x00;
								break;
								case 1:
								PORTB = (PORTB & 0xFC) | 0x01;
								break;
								case 2:
								PORTB = (PORTB & 0xFC) | 0x03;
								break;
								case 3:
								PORTB = (PORTB & 0xFC) | 0x02;
								break;
							}
							ledState++;
							if (ledState == 4)
							{
								ledState = 0;
							}
						}
						break;
						
						case TIMER_TIMEOUT:
						{
							PRINT("Timeout!\n");
							setPower(PowerLow);
//							setSwitch(SwitchOff);
							state_next = StateError;
						}
						break;
						
						case TIMER_ADC:
						{
							timer_start(timer_id, TIMER_SW_DELAY_MS(50));
							int sw_in = portGet(PwrSwitch_in);
							if (sw_in != sw_in_last)
							{
								PRINT ("PwrSwitch_in = %d\n", portGet(PwrSwitch_in));
								if (sw_in == 0)
								{
									if (state == StateError)
									{
										state_next = StateNormal;
									}
								}
							}
							sw_in_last = sw_in;
							adc_start_conversion(0x06);
						}
						break;
					}
				}
				break;

				case NOP:
				break;
			}
		}
		if (state != state_next)
		{
			PRINT("State change \"%s\" => \"%s\"\n", state_str[state], state_str[state_next]);
		}
		state = state_next;

    }
}

