#include "common.h"
#include "bus/usart.h"
#include "bus/mosfet.h"
#include "bus/pwm.h"
#include "bus/i2c.h"

#include <avr/interrupt.h>

// TODO: Implement optional CRC8 sensor measurement check.
// TODO: Either implement software PWM for the FAN03 timer
// (which will be quite complicated) or pick a chip with
// more than two 16-bit PWM outputs like the ATmega328PB.
// https://www.mikrocontroller.net/articles/Soft-PWM
// TODO: Check if FAN02 is receiving the right frequency.
// TODO: Write an improved command parser (low priority).
// TODO: Proper error handling and recovery (after testing).

static int Init(void)
{
	// MOSFETS control things like the heating element
	// so they are the highest priority to initialize
	// to a default state via MOS_Init().

	MOS_Init();

	// The serial interface is required for output
	// functions like Info() and Error() and it uses
	// IRQs, so we need to initialize it as soon as
	// possible and make sure to enable interrupts.

	USART_Init();
	sei();

	Info("Initializing...");

	// The watchdog timer is clocked from a separate
	// on-chip oscillator which runs at 1 MHz. Eight
	// different clock cycle periods can be selected
	// to determine the reset period. If the reset
	// period expires, the chip resets and executes
	// from the reset vector.

	// The update loop must call WDT_Reset to reset
	// the timer, kind of like a dead man's switch
	// allowing us to detect infinite loops and any
	// other error that halts execution.

	if (WDT_HasTriggered())
		Info("Unexpected system reset.");

	WDT_Enable();
	WDT_SetTimeoutFlag(WDT2000); // 2 seconds

	// There is a possiblity to use interrupt signals
	// for I2C communication but only as one large
	// branching routine for the whole I2C system.
	// The blocking approach used right now is fine.

	I2C_Init();
	PWM_Init();

	MOS_Enable(MOS03); // Lights
	// MOS_Enable(MOS01); // Peltier
	// MOS_Disable(MOS02); // Heating

	// Only FAN01 and FAN02 are receiving the correct
	// frequency (25 KHz) right now. The 16-bit timer on
	// the ATMega32A has two outputs so it would require
	// software PWM to have a variable frequency on PD7.

	// A simple implementation will take up around 30-50
	// percent of CPU time. Faster approaches are quite
	// complicated so it might be worth it to switch to
	// something like an ATmega328PB.

	PWM_SetValue(FAN01, 50); // Fan Peltier Hot side
	PWM_SetValue(FAN02, 50); // Fan Peltier Cold Side
	// PWM_SetValue(FAN03, 20); // Fan Heating

	// The I2C_SetChannel command changes the channel
	// setting of the PCA9546 I2C multiplexer. Any
	// command after it will be sent to the device
	// listening on that channel.

	I2C_SetChannel(AHT01);
	I2C_AHT20_Init();

	I2C_SetChannel(AHT02);
	I2C_AHT20_Init();

	I2C_SetChannel(AHT03);
	I2C_AHT20_Init();

	return 0;
}

static void Update(void)
{
	float temp[3], rh[3];
	float adct[3];

	// Info("Reading sensor values...");

	I2C_SetChannel(AHT01);
	I2C_AHT20_Read(&temp[0], &rh[0]);

	I2C_SetChannel(AHT02);
	I2C_AHT20_Read(&temp[1], &rh[1]);

	I2C_SetChannel(AHT03);
	I2C_AHT20_Read(&temp[2], &rh[2]);

	Sleep(200); // FIXME: Poll config register until BIT(15) == 1
	adct[0] = I2C_ADS1115_ReadThermistor(ADS01);
	Sleep(200); // FIXME: Remove me - see above.
	adct[1] = I2C_ADS1115_ReadThermistor(ADS02);
	Sleep(200); // FIXME: Remove me - see above.
	adct[2] = I2C_ADS1115_ReadThermistor(ADS03);

	Info("TEMP0=%.2fC, RH0=%.2f%%", temp[0], rh[0]);
	Info("TEMP1=%.2fC, RH1=%.2f%%", temp[1], rh[1]);
	Info("TEMP2=%.2fC, RH2=%.2f%%", temp[2], rh[2]);
	Info("ADCT1=%.2fC, ADCT2=%.2fC", adct[0], adct[1]);
	Info("ADCT3=%.2fC", adct[2]);

	// TODO: Implement state machine

	// Inputs: TEMP, DEWP
	// States: IDLE, DEHUMIDIFY, HEATUP, COOLDOWN
}

int main(void)
{
	Init();

	for (;;) {
		WDT_Reset();
		Update();
		WDT_Reset();
		Sleep(1000);
	}

	return 0;
}
