#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "led.h"
#include "spi.h"
#include "nrf24l01.h"
#include "nrf24l01_definitions.h"
#include "bme280_interface.h"
#include "bme280_defs.h"
#include "pin_programming.h"
#include "crc.h"
#include "encryption.h"



uint8_t ownId;
const uint8_t encryptionKey[16] = {0x9e, 0x37, 0x79, 0xb9, 0x9b, 0x97, 0x73, 0xe9, 0xb9, 0x79, 0x37, 0x9e, 0x6b, 0x69, 0x51, 0x56}; /* TODO: use exernal file with the keys */
uint64_t salt;

char bool_case = 0;
int timer = 0;
int timer_max = 0;

volatile uint8_t cycle = 0;

volatile bool nrfInterruptRaised;

volatile uint8_t interruptCounter;
volatile uint8_t executionFlag;



static PACKET reportPacket;


void Enter_Power_Save_Mode(void);
void Exit_Power_Save_Mode(void);


/* TODO Notes for power saving:
 * - Power-save-mode needed -> SM2...0 bits written to 011
 * - Entering by issuing the SLEEP instruction -> What call in C?
 *   - Before executing the SLEEP instruction, write bit SE of the SMCR to 1
 * - Let Timer/Counter2 run with the necessary period and enable an interrupt.
 *   -> The Global Interrupt Enable bit in SREG has to be set.
 * - asynchronous/synchronous clock source?
 * - When waking up
 *   - Set PRR bits for needed peripherals
 * - 
 */
void Set_Up_Power_Save_Mode(void);
void Enter_Power_Save_Mode(void);


ISR(TIMER2_COMPA_vect)
{
	/* Do nothing as the interrupt is only used to wake up the MCU. */
}

ISR(PCINT2_vect)
{
	nrfInterruptRaised = true;
}

int main (void)
{
	struct bme280_data sensorData;
	uint16_t crc;

	/* Enable the debug LED */
	LED_DDR |= (1 << LED_PIN);

	/* Get the own ID */
	Configure_Pin_Programming_Pins();
	ownId = Get_Own_Identifier();

	/* Initialize the SPI */
	Initialize_SPI();

	/* Initialize the nrf24l01 */
	Initialize_NRF24L01();
	// The NRF24L01 is now in the mode Standby-I.

	/* Configure the transmission parameters (Enhanced ShockBurst)*/
	Configure_Transmission(ownId);

	/* Initialize the BME280 */
	Initialize_BME280();
	Set_Up_Power_Save_Mode();

	/* Initialize the salt */
	salt = 0xFFFFFFFFFFFFFFFFull;
	salt &= ~ownId;

	/* Delay the change of the operating frequency by the function Enter_Power_Save_Mode for the
	 * first function pass. If it is changed before the ISP can flash the MCU the clocks of the ISP
	 * and MCU are mismatched and the flashing will fail.
	 */
	_delay_ms(500);

	while(1)
	{
		Enter_Power_Save_Mode(); // The MCU enters the Power Save Mode here.
		Exit_Power_Save_Mode(); // The MCU exits the Power Save Mode here.

		if (cycle == 0) // TODO cycle == 7 to execute this every 60 s
		{
			/* Re-Initialize the peripherals */
			Set_BME280_Pins();
			Set_NRF24L01_Pins();

			/* Get measurement and send it */
			BME280_Get_Measurement(&sensorData);


			memset((uint8_t*)&reportPacket, 0, sizeof(reportPacket)); //Reinitialize the buffer with zeros

			salt &= ~(1ull<<55);
			reportPacket.salt = salt;
			reportPacket.payload.values.packetIdentifier.elementCount = 3;
			reportPacket.payload.values.packetIdentifier.packetType = PACKET_TYPE_REPORT;

			/* Fill in the payload */
			reportPacket.payload.values.valueTypeTemperature = VALUE_TYPE_TEMPERATURE;
			reportPacket.payload.values.temperature = sensorData.temperature/10;
			reportPacket.payload.values.valueTypePressure = VALUE_TYPE_PRESSURE;
			reportPacket.payload.values.pressure = sensorData.pressure;
			reportPacket.payload.values.valueTypeHumidity = VALUE_TYPE_HUMIDITY;
			reportPacket.payload.values.humidity = sensorData.humidity * 100/1024;

			/* Calculate the CRC */
			crc = Calculate_Crc(reportPacket.payload.buffer, PACKET_BUFFER_LENGTH);
			reportPacket.crc = crc;

			/* Encrypt the packet */
			/* TODO: 
			 * - increment the salt for every packet
			 * - Receive salt from the base station
			 */
			Encrypt((uint32_t*) &reportPacket.payload.buffer,
			        PACKET_BUFFER_LENGTH + sizeof(crc),
			        salt, (uint32_t*) encryptionKey);

			NRF24L01_Send_Message((uint8_t*)&reportPacket, sizeof(reportPacket));

			_delay_ms(100); /* TODO: only for debugging, remove this later! */
			LED_PORT &= ~(1 << LED_PIN);

			cycle = 0;
		}
		else
		{
			cycle ++;
		}
	}
}

void Set_Up_Power_Save_Mode(void)
{
	// Disable Brown-Out-Detection by setting the BODLEVEL 2:0 Fuses to 0b111 (should be default)
	// Disable the on-chip debug system by setting the DWEN Fuse to 1 (should be default)

	/* Disable some unused peripherals that are not used during operation of the weather station: */
	// The ADC is turned off by default
	ACSR &= ~(1<<ACD); // Analog comperator
	ACSR &= ~(1<<ACIE); // The interrupt bit has to be cleared after switchin of the analog comperator
	/*
	 * The Internal voltager reference is automatically disabled if the BOD, ADC and Voltage
	 * Reference are disabled
	 */

	/* Set up Timer/Counter2 */
	PRR &= ~(1<<PRTIM2); // Enable the timer 2 in the Power Reduction Register

	TCCR2A |= (1<<COM2A1)|(1<<COM2A0)|(1<<WGM21); // Set the timer to ClearTimer on Compare Match with output compare mode for channel A
	/* There is a problem: The maximum time until the counter 2 overflows is 262ms at the nominal
	 * core frquency of 1MHz and a timer prescaler of 1024.
	 * This can be attenuated by lowering the system frequency to 31.25 kHz via the clock prescaler
	 * (CLKPS3...0) of the register CLKPR. This gives a overflow time of 8.3s.
	 * The cycle time of one minute can thus be accomplished by setting the output compare register
	 * to 229. This spawns an interrupt every 7.5s which means the operation has to be executed
	 * every 8 interrupt calls.
	 */

	TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20); // Set the timer prescaler to 1/1024
	OCR2A = 229; // TODO: calculate this number from the wanted cycle time and the timer frequency.

	TIMSK2 |= (1<<OCIE2A); // Enable the Output Compare Match A Interrupt.

	/* Enable global interrupts: */
	sei();
}

void Enter_Power_Save_Mode(void)
{
	PRR |= (1<<PRTWI) | (1<<PRTIM0) | (1<<PRTIM1) | (1<<PRSPI) | (1<<PRUSART0) | (1<<PRADC); /* Only timer 2 is needed for wake-up interrupt */

	/* Set the clock prescaler for a frequency of 32.25 kHz*/
	CLKPR = (1<<CLKPCE);
	CLKPR = (1<<CLKPS3);

	TCNT2 = 0;// Reset timer 2
	TIMSK2 |= (1<<OCIE2A); // Enable the Output Compare Match A Interrupt.


	set_sleep_mode(SLEEP_MODE_PWR_SAVE);
	sleep_mode();
}

void Exit_Power_Save_Mode(void)
{
	TIMSK2 &= ~(1<<OCIE2A); // Disable the Output Compare Match A Interrupt.

	/* Set the normal operating frequency of 1 MHz */
	CLKPR = (1<<CLKPCE);
	CLKPR = (1<<CLKPS1) | (1<<CLKPS0);

	PRR &= ~(1<<PRSPI); // Enable SPI
	Initialize_SPI(); // reinitalize SPI
}
