/*
  ______                              _
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
    (C)2016 Semtech

Description: Handling of the node configuration protocol

License: Revised BSD License, see LICENSE.TXT file include in the project

Maintainer: Miguel Luis, Gregory Cristian and Matthieu Verdy
*/
#include "sx1280-hal.h"
#include <functional>
#include <FreeRTOS.h>
#include <task.h>
#include <cstring>

/*!
 * \brief Used to block execution waiting for low state on radio busy pin.
 *        Essentially used in SPI communications
 */
#define WaitOnBusy( )          for(uint8_t val=1; val; BUSY->read(&val));

// This code handles cases where assert_param is undefined
#ifndef assert_param
#define assert_param( ... )
#endif

SX1280Hal::SX1280Hal( SPI_Peripheral* pSpiPeripheral, GPIO_Pin* pNssPin,
			GPIO_Pin* pBusyPin, GPIO_Pin* pIrqPin, GPIO_Pin* pRstPin,
			RadioCallbacks_t *callbacks ) :
			SX1280( callbacks ),
			RadioSpi(pSpiPeripheral),
			RadioNss( pNssPin ),
			RadioReset( pRstPin ),
			BUSY( pBusyPin ),
			DIO1( pIrqPin )
{

}

SX1280Hal::~SX1280Hal( void )
{

};

void SX1280Hal::SpiInit( void )
{
    RadioNss->set();
}

void SX1280Hal::IoIrqInit( DioIrqHandler irqHandler )
{
    assert_param( RadioSpi != NULL );
    SpiInit( );
	DIO1->registerExtiCallback(this);
}

void SX1280Hal::Reset( void )
{
	setDioIrqEnabled( false );
    vTaskDelay( 20 );
    RadioReset->reset();
    vTaskDelay( 50 );
    RadioReset->set();
    vTaskDelay( 20 );
    setDioIrqEnabled( true );
}

void SX1280Hal::Wakeup( void )
{
	setDioIrqEnabled( false );

    //Don't wait for BUSY here

    if( RadioSpi != NULL )
    {
    	RadioSpi->take(100);
        RadioNss->reset();
        uint8_t txBuffer[2] = {RADIO_GET_STATUS, 0};
        RadioSpi->transmit(txBuffer, 2);
        RadioNss->set();
        RadioSpi->give();
    }

    // Wait for chip to be ready.
    WaitOnBusy( );

    setDioIrqEnabled( true );
}

void SX1280Hal::WriteCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
{
    WaitOnBusy( );

    if( RadioSpi != NULL )
    {
    	RadioSpi->take(100);
        RadioNss->reset();
        uint8_t txBuffer[size+1];
        txBuffer[0] = command;
        memcpy(txBuffer+1, buffer, size);
        RadioSpi->transmit(txBuffer, size+1);
        RadioNss->set();
        RadioSpi->give();
    }

    if( command != RADIO_SET_SLEEP )
    {
        WaitOnBusy( );
    }
}

void SX1280Hal::ReadCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
{
    WaitOnBusy( );

    if( RadioSpi != NULL )
    {
    	RadioSpi->take(100);
        RadioNss->reset();
        if( command == RADIO_GET_STATUS )
        {
        	uint8_t txBuffer[3] = {RADIO_GET_STATUS, 0, 0};
        	uint8_t rxBuffer[3];
        	RadioSpi->trx(txBuffer, rxBuffer, 3);
        	buffer[0] = rxBuffer[0];
        }
        else
        {
        	uint8_t txBuffer[size+2];
        	uint8_t rxBuffer[size+2];
        	memset(txBuffer, 0, size+2);
        	txBuffer[0] = command;
        	RadioSpi->trx(txBuffer, rxBuffer, size+2);
        	memcpy(buffer, rxBuffer+2, size);
        }
        RadioNss->set();
        RadioSpi->give();
    }

    WaitOnBusy( );
}

void SX1280Hal::WriteRegister( uint16_t address, uint8_t *buffer, uint16_t size )
{
    WaitOnBusy( );

    if( RadioSpi != NULL )
    {
    	RadioSpi->take(100);
        RadioNss->reset();
        uint8_t txBuffer[size+3];
        txBuffer[0] = RADIO_WRITE_REGISTER;
        txBuffer[1] = ( address & 0xFF00 ) >> 8;
        txBuffer[2] = address & 0x00FF;
        memcpy(txBuffer+3, buffer, size);
        RadioSpi->transmit(txBuffer, size+3);
        RadioNss->set();
        RadioSpi->give();
    }

    WaitOnBusy( );
}

void SX1280Hal::WriteRegister( uint16_t address, uint8_t value )
{
    WriteRegister( address, &value, 1 );
}

void SX1280Hal::ReadRegister( uint16_t address, uint8_t *buffer, uint16_t size )
{
    WaitOnBusy( );

    if( RadioSpi != NULL )
    {
    	RadioSpi->take(100);
        RadioNss->reset();
        uint8_t txBuffer[size+4];
        uint8_t rxBuffer[size+4];
        txBuffer[0] = RADIO_READ_REGISTER;
        txBuffer[1] = ( address & 0xFF00 ) >> 8;
        txBuffer[2] = address & 0x00FF;
        memset(txBuffer+3, 0, size+1);
        RadioSpi->trx(txBuffer, rxBuffer, size+4);
        memcpy(buffer, rxBuffer+4, size);
        RadioNss->set();
        RadioSpi->give();
    }

    WaitOnBusy( );
}

uint8_t SX1280Hal::ReadRegister( uint16_t address )
{
    uint8_t data;

    ReadRegister( address, &data, 1 );
    return data;
}

void SX1280Hal::WriteBuffer( uint8_t offset, uint8_t *buffer, uint8_t size )
{
    WaitOnBusy( );

    if( RadioSpi != NULL )
    {
    	RadioSpi->take(100);
        RadioNss->reset();
        uint8_t txBuffer[size+2];
        txBuffer[0] = RADIO_WRITE_BUFFER;
        txBuffer[1] = offset;
        memcpy(txBuffer+2, buffer, size);
        RadioSpi->transmit(txBuffer, size+2);
        RadioNss->set();
        RadioSpi->give();
    }

    WaitOnBusy( );
}

void SX1280Hal::ReadBuffer( uint8_t offset, uint8_t *buffer, uint8_t size )
{
    WaitOnBusy( );

    if( RadioSpi != NULL )
    {
    	RadioSpi->take(100);
        RadioNss->reset();
        uint8_t txBuffer[size+3];
        uint8_t rxBuffer[size+3];
        txBuffer[0] = RADIO_READ_BUFFER;
        txBuffer[1] = offset;
        memset(txBuffer+2, 0, size+1);
        RadioSpi->trx(txBuffer, rxBuffer, size+3);
        memcpy(buffer, rxBuffer+3, size);
        RadioNss->set();
        RadioSpi->give();
    }

    WaitOnBusy( );
}

uint8_t SX1280Hal::GetDioStatus( void )
{
	uint8_t inter =0;
	uint8_t retval = 0;
	DIO1->read(&retval);
	retval = retval<<1;
	BUSY->read(&inter);
	retval += inter;
    return retval;
}

void SX1280Hal::setDioIrqEnabled(bool enabled) {
	dioIrqEnabled = enabled;
}

void SX1280Hal::irqHandler(InterruptReason *reason) {

}
