#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdbool.h>

#include "led.h" // TODO: for debugging
#include "spi.h"
#include "nrf24l01.h"
#include "nrf24l01_definitions.h"

/* TODO
 * - Send functions
 * - Interrupt handling for Send
 */

extern volatile bool nrfInterruptRaised;

void Print_Register_Contents(uint8_t address);
void Send_TX_Flush_Command(void);

static void Write_Two_Bytes(uint8_t byte1, uint8_t byte2);
static void Write_Byte_And_Buffer(uint8_t byte, uint8_t * buffer, uint8_t length);

/* Startup and initial configuration of the NRF24L01 */
void Initialize_NRF24L01(void)
{
	CONFIG_REGISTER configRegisterContents = {.byte = 0x0};

	/* Configure the AVR pins for the nrf24l01 */
	Set_NRF24L01_Pins();

	/* Wait more than 10.3 ms to make sure the nrf24l01 is running */
	_delay_ms(11);

	/* Write the PWR_UP bit of the CONFIG register (EN_CRC is also set) */
	configRegisterContents.bits.EN_CRC = 0x1;
	configRegisterContents.bits.PWR_UP = 0x1;
	Write_NRF_Register(CONFIG_ADDRESS, configRegisterContents.byte);

	/* Wait more than 1.5 ms for the change to take effect */
	_delay_ms(2);

	/* The NRF24L01 is now in the mode Standby-I */
}

void Set_NRF24L01_Pins(void)
{
	/* Set up the NRF24L01 */
	NRF_CE_DDR |= (1 << NRF_CE_PIN);
	NRF_CSN_DDR |= (1 << NRF_CSN_PIN);

	/* Set the chip select pin to not selected */
	NRF_CSN_PORT |= (1 << NRF_CSN_PIN);

	/* Ensure that the CE pin is set to 0*/
	NRF_CE_PORT &= ~(1 << NRF_CE_PIN);

	/* Set the interrupt pin */
	/* TODO: PCINT21 -> PCINT2 */
	NRF_IRQ_DDR &= ~(1 << NRF_IRQ_PIN); // Set the pin as input
	NRF_IRQ_PORT |= (1 << NRF_IRQ_PORT); // Enable the pullup for the pin
}

void Configure_Transmission(uint8_t moduleId)
{
	FEATURE_REGISTER featureRegisterContents = {.byte = 0x0};
	DYNPD_REGISTER dyndpRegisterContents = {.byte = 0x0};
	SETUP_RETR_REGISTER setupRetrRegisterContents = {.byte = 0x0};

	uint8_t txAddress[5] = {0xB3, 0xB3, 0xB3, 0xB3, 0x00};
	uint8_t rx0Address[5] = {0xB3, 0xB3, 0xB3, 0xB3, 0x20};
	/* 
	 * - Length of CRC (CRCO in CONFIG)
	 * - Enable auto acknowledgment (EN_AA)
	 *   -> Register already set correctly after reset
	 * - Enable data pipes (EN_RXADDR)?
	 *   -> Two pipes are already enabled on reset
	 * - Set up address width (SETUP_AW)
	 *   -> 3 bytes
	 * - Automatic Retransmission (SETUP_RETR)
	 *   -> ARD = 0b0000
	 *   -> 3 retransmits -> ARC = 0b0011
	 *   -> Register already set correctly after reset
	 * - RF Channel (RF_CH)
	 *   -> RF_CH = 0b1010000
	 * - RF Setup (RF_SETUP)
	 *   -> first use reset values, can be fine tuned later
	 * - Enable dynamic payload length (DYNPD) -> command activate + 0x73, then set bits in FEATURE?
	 */

	/* Set the address width to 3 bytes */
	//Write_NRF_Register(0x03, 0x1);

	/* Set the frequency to 1450 MHz */
	Write_NRF_Register(RF_CH_ADDRESS, 0x32);

	/* Enable dynamic payload length */
	Send_Activate_Command();
	featureRegisterContents.bits.EN_DPL = 1; // enable dynamic payload length
	Write_NRF_Register(FEATURE_ADDRESS, featureRegisterContents.byte);

	/* */
	setupRetrRegisterContents.bits.ARC = 0x3;
	setupRetrRegisterContents.bits.ARD = 0xF;
	Write_NRF_Register(SETUP_RETR_ADDRESS, setupRetrRegisterContents.byte);

	/* set dynamic payload length for all data pipes */ // TODO: only pipe 0 is currently in use -> don't set the other values
	dyndpRegisterContents.bits.DPL_P0 = 1;
	dyndpRegisterContents.bits.DPL_P1 = 1;
	dyndpRegisterContents.bits.DPL_P2 = 1;
	dyndpRegisterContents.bits.DPL_P3 = 1;
	dyndpRegisterContents.bits.DPL_P4 = 1;
	dyndpRegisterContents.bits.DPL_P5 = 1;
	Write_NRF_Register(DYNPD_ADDRESS, dyndpRegisterContents.byte);

	/* Set the TX address */
	Set_TX_Address(txAddress, MAX_ADDRESS_LENGTH);

	rx0Address[4] = moduleId; // The last byte of the address corresponds to the Id set by the pin programming
	Set_RX_P0_Address(rx0Address, MAX_ADDRESS_LENGTH);


	PCMSK2 |= (1<<PCINT21); // Set the external interrupt for PD5
}

void NRF24L01_Send_Message(uint8_t *buffer, uint8_t length)
{
	STATUS_REGISTER statusRegisterContents = {.byte = 0x0};
	
	if ((length > 32) || (length == 0))
	{
		return;
	}

	PCICR |= (1<<PCIE2); // Enable the interrupt for the IRQ signal

	Write_Message_To_TX_FIFO(length, buffer);

	/* Set CE = 1 for more than 10 us */
	NRF_CE_PORT |= (1 << NRF_CE_PIN);
	_delay_us(15);
	NRF_CE_PORT &= ~(1 << NRF_CE_PIN);

	while (nrfInterruptRaised == false); // Wait until the transmission is complete
	/* An interrupt instead of polling the status register is used to avoid transmission errors
	 * induced by the SPI:
	 * https://forum.mysensors.org/topic/10452/nrf24l01-communication-failure-root-cause-and-solution
	 */
	LED_PORT |= (1 << LED_PIN);

	statusRegisterContents.byte = Read_NRF_Status_Register();


	if (statusRegisterContents.bits.MAX_RT == 1)
	{
		Send_TX_Flush_Command(); /* Remove the packet from the TX FIFO as it is not done automatically */
	}
	

	/* Reset the interrupts */
	statusRegisterContents.bits.TX_DS = 1;
	statusRegisterContents.bits.MAX_RT = 1;
	statusRegisterContents.bits.RX_DR = 1;
	Write_NRF_Register(STATUS_ADDRESS, statusRegisterContents.byte);

	PCICR &= ~(1<<PCIE2); // Disable the interrupt for the IRQ signal
	nrfInterruptRaised = false;


	return;
}

uint8_t Read_NRF_Status_Register(void)
{
	uint8_t registerContents;

	SPI_Start_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);
	registerContents = SPI_Transfer_Byte(0x00);
	SPI_Stop_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);
	return registerContents;
}


/* TODO: rewrite the read register function if it is needed (remove the read operations for the 5-byte registers)*/
#if 0
uint8_t Read_NRF_Register(uint8_t address, uint8_t * registerContents)
{
	/* TODO: simplify this function, as the registers with more than one byte are accessed with other functions */
	uint8_t numberOfBytes = 0;

	if ((address == 0x0A) ||
	    (address == 0x0B) ||
	    (address == 0x10))
	{
		numberOfBytes = 5;
	}
	else
	{
		numberOfBytes = 1;
	}

	/* First write the address */
	SPI_Start_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);


	SPI_Transfer_Byte(address);

	/* Read the register bytes */
	for (uint8_t i = 0; i < numberOfBytes; i++)
	{
		/* Write dummy data to shift in the register content */
		registerContents[i] = SPI_Transfer_Byte(0x0);
	}

	SPI_Stop_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);

	return numberOfBytes;
}

#endif

void Write_NRF_Register(uint8_t address, uint8_t registerContents)
{
	Write_Two_Bytes(address | 0x20, registerContents);
}

void Send_Activate_Command(void)
{
	Write_Two_Bytes(0x50, 0x73);
}

static void Write_Two_Bytes(uint8_t byte1, uint8_t byte2)
{
	SPI_Start_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);

	SPI_Transfer_Byte(byte1);
	SPI_Transfer_Byte(byte2);

	SPI_Stop_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);
}

void Send_TX_Flush_Command(void)
{
	/* First write the write command with the address */
	SPI_Start_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);

	SPI_Transfer_Byte(0xE1);

	SPI_Stop_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);
}



void Write_Message_To_TX_FIFO(uint8_t length, uint8_t * buffer)
{
	Write_Byte_And_Buffer(0xA0, buffer, length);
}

void Set_TX_Address(uint8_t * txAddress, uint8_t length)
{
	Write_Byte_And_Buffer(TX_ADDR_ADDRESS | 0x20, txAddress, length);
}

void Set_RX_P0_Address(uint8_t * rxAddress, uint8_t length)
{
	Write_Byte_And_Buffer(RX_ADDR_P0_ADDRESS | 0x20, rxAddress, length);
}

static void Write_Byte_And_Buffer(uint8_t byte, uint8_t * buffer, uint8_t length)
{
	SPI_Start_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);

	SPI_Transfer_Byte(byte);

	/* Write the data byte */
	for (uint8_t i = 0; i < length; i ++)
	{
		SPI_Transfer_Byte(buffer[i]);
	}

	SPI_Stop_Transmission(&NRF_CSN_PORT, NRF_CSN_PIN);
}

//TODO: only write the used bytes into the address registers & add generic write functions
