/*
 * File:   btle_uart.c
 * Author: Brandon Mitchell
 *
 * Created on February 9, 2015, 2:57 PM
 *
 * Bluetooth® LE UART and receiver FIFO implementation,
 * including sending commands and receiving data
 *
 */

#include "version.h"
#include "btle.h"
//#include "led.h"
#include "timer.h"
//#include "config.h"
#include <xc.h>

////////////////////////////////////////////////////////////////////////////////
// Constants

#define BTLE_WAIT_FOR_REPLY_ms 200

////////////////////////////////////////////////////////////////////////////////
// Public Data

btleUart_t btleUart;

////////////////////////////////////////////////////////////////////////////////
// Local Function Declarations

exitCondition_t TxCharUart( const unsigned char txChar );
exitCondition_t RxCharUart( void );
exitCondition_t ReadRxFifo( char * rxFifoString, const unsigned char numCharsRequested );
void            SetBtleFlag( const unsigned char flag );
void            ClearBtleFlag( const unsigned char flag );
exitCondition_t CompareStrings( const unsigned char * s1, const unsigned char * s2 );
exitCondition_t SendBtleCommand( const unsigned char command );
exitCondition_t TxStringUart( const unsigned char * txString );


////////////////////////////////////////////////////////////////////////////////
// Interface Function Definitions

// Init UART, RN4020 control signals, and receiver FIFO
// Modify this function for specific MCU and hardware configuration
void InitBtleUart( void )
{
    TRISBbits.TRISB15 = 1;
    RPINR18bits.U1RXR = 15;			// RB15 tied to U1RX
    RPOR7bits.RP14R = 0b00011;		// RP14/RB14/Pin25 tied to U1TX
	U1MODEbits.STSEL = 0;			// 1 Stop bit
	U1MODEbits.PDSEL = 0;			// No Parity, 8 data bits
	U1MODEbits.ABAUD = 0;			// Auto-Baud Disabled
#ifdef _INIT_RN4020_CONFIG
	U1MODEbits.BRGH = 1;			// High Speed mode
    U1BRG = 42;                     // BAUD Rate Setting for 115200, BRGH=1 UxBRG = ((Fcy/Baudrate)/4)-1 = 20000000/115200/4-1 = 42
#else
	U1MODEbits.BRGH = 0;			// Standard Speed mode
	U1BRG = 520;					// BAUD Rate Setting for 2400, BRGH=0 UxBRG = ((Fcy/Baudrate)/16)-1 = 20000000/2400/16-1 = 520
#endif
	U1STAbits.UTXISEL0 = 1;			// UTXISEL<1:0> = 01 = Interrupt when all transmit operations are completed.
	U1STAbits.UTXISEL1 = 0;
	IPC3bits.U1TXIP = 2;			// UART Transmitter interrupt priority
	IPC2bits.U1RXIP = 2;			// UART Receiver interrupt priority
	IFS0bits.U1TXIF = 0;			// Clear UART Transmitter interrupt flag
	IFS0bits.U1RXIF = 0;			// Clear UART Receiver interrupt flag
	IEC0bits.U1TXIE = 0;			// Disable UART Transmitter interrupt
	IEC0bits.U1RXIE = 1;			// Enable UART Receiver interrupt
	U1MODEbits.UARTEN = 1;			// Enable UART
	U1STAbits.UTXEN = 1;			// Enable UART TX, wait at least 104 usec (1/9600) before sending first char
    
    btleUart.rxFifoInIndex  = 0;
    btleUart.rxFifoOutIndex = 0;
    btleUart.rxBufferSize   = 0;
    btleUart.numLinesReady  = 0;
    ClearBtleFlag(BTLE_STATUS_ALL_FLAGS);
}

// Restore RN4020 to factory default
exitCondition_t BtleFullFactoryReset( void ) {

    WAKE_SW = 1;
    TimerDelay_ms(500);
    WAKE_HW = 1;
    TimerDelay_ms(800);
    WAKE_HW = 0;
    TimerDelay_ms(800);
    WAKE_HW = 1;
    TimerDelay_ms(800);
    WAKE_HW = 0;
    TimerDelay_ms(800);
    WAKE_HW = 1;
    TimerDelay_ms(800);
    WAKE_HW = 0;
    TimerDelay_ms(800);
    WAKE_HW = 1;
    TimerDelay_ms(800);
    WAKE_HW = 0;
    TimerDelay_ms(800);
    WAKE_SW = 0;
    TimerDelay_ms(500);
    return EXIT__SUCCESS;
}

// Configures RN4020 as a demo board - only need to run once
exitCondition_t ConfigureBtleCentral( void ) {

    exitCondition_t status = 0;
    WAKE_SW = 1;
    TimerDelay_ms( 1500 );
    ClearRxFifo();
    //status =  HandleBtleCommand( SET_FACTORY_DEFAULT );
    status  = HandleBtleCommand( SET_CENTRAL_NAME );
    status |= HandleBtleCommand( SET_CENTRAL_SERVICE );
    status |= HandleBtleCommand( SET_CENTRAL_RESOURCE );
    status |= HandleBtleCommand( SET_BAUD_RATE );
    status |= HandleBtleCommand( CLEAR_PRIVATE_SERVICE );
    status |= HandleBtleCommand( SET_PRIVATE_SERVICE );
    status |= HandleBtleCommand( SET_PRIVATE_CHARACTERISTIC );
    status |= HandleBtleCommand( REBOOT );
    TimerDelay_ms( 2000 );
    WAKE_SW = 0;
    if( status ) {
        return EXIT__FAILURE;
    } else {
        return EXIT__SUCCESS;
    }
}

// Configures RN4020 as a hub - only need to run once
exitCondition_t ConfigureBtlePeripheral( void ) {

    exitCondition_t status = 0;
    WAKE_SW = 1;
    TimerDelay_ms( 1500 );
    ClearRxFifo();
    //status =  HandleBtleCommand( SET_FACTORY_DEFAULT );
    status  = HandleBtleCommand( SET_PERIPHERAL_NAME );
    status |= HandleBtleCommand( SET_PERIPHERAL_SERVICE );
    status |= HandleBtleCommand( SET_PERIPHERAL_RESOURCE );
    status |= HandleBtleCommand( SET_BAUD_RATE );
    status |= HandleBtleCommand( CLEAR_PRIVATE_SERVICE );
    status |= HandleBtleCommand( REBOOT );
    TimerDelay_ms( 2000 );
    WAKE_SW = 0;
    if( status ) {
        return EXIT__FAILURE;
    } else {
        return EXIT__SUCCESS;
    }
}

// Sends a BTLE command to the RN4020 and reads the response from the receiver FIFO
exitCondition_t HandleRawBtleCommand( const btleCommand_t * command ) {

    exitCondition_t e;
    if( command->commandString ) {
        e = SendRawBtleCommand( command );
        TimerDelay_ms( BTLE_WAIT_FOR_REPLY_ms );
    }
    while( btleUart.rxBufferSize > 0 ) {
        e |= ReadAllRxFifo( btleUart.rxResponse );
    }
    return e;
}

// Sends a BTLE command to the RN4020
exitCondition_t SendRawBtleCommand( const btleCommand_t * command ) {

    return TxStringUart( command->commandString );
}

// Sends a standard BTLE command from the btleCommandTable[] in btle_command.c.
// Compares response to expected response from btleCommandTable[]
exitCondition_t HandleBtleCommand( const unsigned char command ) {

    SendBtleCommand( command );
    TimerDelay_ms( BTLE_WAIT_FOR_REPLY_ms );
    int length = (int)btleCommandTable[command].responseLength;

    while( btleUart.rxBufferSize > 0 && length >= 0 ) {
        ReadRxFifo( btleUart.rxResponse, ( length > BTLE_UART_RX_LINE_SIZE ? BTLE_UART_RX_LINE_SIZE : (unsigned char)length ) );
        length -= BTLE_UART_RX_LINE_SIZE;
        if( CompareStrings( btleUart.rxResponse, btleCommandTable[command].responseString ) == EXIT__SUCCESS ) {
            if( btleUart.rxBufferSize == 0 ) {
                return EXIT__SUCCESS;
            }
        } else {
            if ( btleUart.rxBufferSize == 0 ) {
                return EXIT__FAILURE;
            }
        }
    }
    return EXIT__FAILURE;
}

// Reads all characters from the receiver FIFO, up to BTLE_UART_RX_LINE_SIZE,
// into rxFifoString
exitCondition_t ReadAllRxFifo( char * rxFifoString ) {

    unsigned char ii;
    for( ii = 0; ii < BTLE_UART_RX_LINE_SIZE; ++ii ) {
        *( rxFifoString + ii ) = btleUart.rxFifo[btleUart.rxFifoOutIndex++];
        --btleUart.rxBufferSize;
        if( btleUart.rxFifoOutIndex == BTLE_UART_RX_BUFFER_SIZE ) {
            btleUart.rxFifoOutIndex = 0;
        }
        if( btleUart.rxBufferSize == 0 ) {
            break;
        }
    }
    *( rxFifoString + ii + 1 ) = '\0';
    ClearBtleFlag( BTLE_STATUS_RX_BUFFER_FULL_FLAG );
    return EXIT__SUCCESS;
}

// Reads a line from the receiver FIFO, up to BTLE_UART_RX_LINE_SIZE characters,
// into rxFifoString
exitCondition_t ReadLineRxFifo( char * rxFifoString ) {

    if( btleUart.rxBufferSize == 0 || btleUart.numLinesReady == 0 ) {
        return EXIT__FAILURE;
    }

    unsigned char ii;
    for( ii = 0; ii < BTLE_UART_RX_LINE_SIZE; ++ii ) {
        *( rxFifoString + ii ) = btleUart.rxFifo[btleUart.rxFifoOutIndex++];
        --btleUart.rxBufferSize;
        if( btleUart.rxFifoOutIndex == BTLE_UART_RX_BUFFER_SIZE ) {
            btleUart.rxFifoOutIndex = 0;
        }

        // bail if receiver FIFO is empty
        if( btleUart.rxBufferSize == 0 ) {
            break;
        }

        // bail when end of line is reached
        if( *( rxFifoString + ii ) == '\n' ) {
            //DisableInterrupts();
            --btleUart.numLinesReady;
            //EnableInterrupts();
            break;
        }
    }
    *( rxFifoString + ii + 1 ) == '\0';
    ClearBtleFlag( BTLE_STATUS_RX_BUFFER_FULL_FLAG );
    return EXIT__SUCCESS;
}


////////////////////////////////////////////////////////////////////////////////
// Local Function Definitions

// reads a number of characters from the receiver FIFO
// Precondition: numCharsRequested has to be less than BTLE_UART_RX_LINE_SIZE,
// and less than rxBufferSize
exitCondition_t ReadRxFifo( char * rxFifoString, const unsigned char numCharsRequested ) {

    if( numCharsRequested > btleUart.rxBufferSize ) {
        return EXIT__FAILURE;
    } else {
        unsigned char ii;
        for( ii = 0; ii < numCharsRequested; ++ii ) {
            *( rxFifoString + ii ) = btleUart.rxFifo[btleUart.rxFifoOutIndex++];
            --btleUart.rxBufferSize;
            if( btleUart.rxFifoOutIndex == BTLE_UART_RX_BUFFER_SIZE ) {
                btleUart.rxFifoOutIndex = 0;
            }
        }
        *( rxFifoString + numCharsRequested ) = '\0';
        ClearBtleFlag( BTLE_STATUS_RX_BUFFER_FULL_FLAG );
        return EXIT__SUCCESS;
    }
}

// Sends a BTLE command from the btleCommandTable[] to the RN4020
exitCondition_t SendBtleCommand( const unsigned char command ) {

    return TxStringUart( btleCommandTable[command].commandString );
}

// Sends a string to the UART one character at a time
exitCondition_t TxStringUart( const unsigned char * txString ) {

    if( btleUart.btleUartFlags == 0 ) {
        int ii = 0;
        while( *( txString + ii ) ) {
            if ( TxCharUart( txString[ii] ) != EXIT__SUCCESS ) {
                SetBtleFlag( BTLE_STATUS_TX_ERROR_FLAG );
            }
            ++ii;
        }
        return EXIT__SUCCESS;
    } else {
#ifdef _DEBUG
        return btleUart.btleUartFlags;
#else
        return EXIT__FAILURE;
#endif
    }
}

// Clears receiver FIFO by resetting indices. Existing data are not deleted.
void ClearRxFifo( void ) {

    btleUart.rxFifoInIndex  = 0;
    btleUart.rxFifoOutIndex = 0;
    btleUart.rxBufferSize = 0;
}

// Transmits a character to the UART
exitCondition_t TxCharUart( const unsigned char txChar ) {

    SetBtleFlag( BTLE_STATUS_TX_FLAG );
//    while( BTLE_UART_TX_IS_BUSY );            // TODO: Add Timeout to escape this while loop
    while (!U1STAbits.TRMT);
    U1TXREG = txChar;
    ClearBtleFlag( BTLE_STATUS_TX_FLAG );
    return EXIT__SUCCESS;
}

// Reads a character from the UART, checking for errors, and updating BTLE_UART flags
exitCondition_t RxCharUart( void ) {

    if( U1STAbits.FERR ) {
        btleUart.junkByte = U1RXREG;

        return EXIT__FAILURE;
    }
    // read char to FIFO
    btleUart.rxFifo[btleUart.rxFifoInIndex] = U1RXREG;
    ++btleUart.rxBufferSize;

    // keep track of number of lines in FIFO
    if( btleUart.rxFifo[btleUart.rxFifoInIndex++] == '\n' ) {
        ++btleUart.numLinesReady;
    }

    if( btleUart.rxFifoInIndex == BTLE_UART_RX_BUFFER_SIZE ) {
        btleUart.rxFifoInIndex = 0;
    }
    if( btleUart.rxBufferSize == BTLE_UART_RX_BUFFER_SIZE ) {
        SetBtleFlag( BTLE_STATUS_RX_BUFFER_FULL_FLAG );
    }
    if( U1STAbits.OERR ) {
        U1STAbits.OERR = 0;
        SetBtleFlag( BTLE_STATUS_OERR_ERROR_FLAG );
    }
    return EXIT__SUCCESS;
}

// Sets a BTLE_UART flag
void SetBtleFlag( const unsigned char flag ) {

    btleUart.btleUartFlags |= flag;
}

// Clears a BTLE_UART flag
void ClearBtleFlag( const unsigned char flag ) {

    btleUart.btleUartFlags &= ~flag;
}

// Compares 2 strings, returning EXIT__SUCCESS if they match, EXIT__FAILURE otherwise
// Also returns EXIT__SUCCESS if string 2 is empty
exitCondition_t CompareStrings( const unsigned char * s1, const unsigned char * s2 ) {
    
    if( !( *s2 ) ) {                    //special case - empty btleCommandTable response string
        return EXIT__SUCCESS;
    }
    int ii = 0;
    while( *( s1 + ii ) && *( s2 + ii ) ) {
        if( *( s1 + ii ) != *( s2 + ii ) ) {
            return EXIT__FAILURE;
        }
        ++ii;
    }
    if( *( s1 + ii ) || *( s2 + ii ) ) {  //one string is longer than the other
        return EXIT__FAILURE;
    }
    return EXIT__SUCCESS;
}

////////////////////////////////////////////////////////////////////////////////
// Interrupt Service Routine

// Modify for particular MCU and hardware configuration
//void __interrupt ISR( void ) {
//
//    // char has been received by BTLE_UART
//    if( BTLE_UART_BYTE_RECEIVED ) {
//        SetBtleFlag( BTLE_STATUS_RX_FLAG );
//        RxCharUart();
//        ClearBtleFlag( BTLE_STATUS_RX_FLAG );
//    }
//
//    // timer 1 interrupts every 10 ms
//    if( TMR1IF ) {
//        TMR1 = TIMER_ONE_INIT_COUNT;
//        TMR1IF = 0;
//        systemTime_ms += systemTick_ms;
//    }
//
//    // pushbutton has been pressed
//    if( IOCAF5 ) {
//        IOCAF5 = 0;
//        // tiny bit of debouncing
//#ifdef HUB_RN4020
//        if( PUSHBUTTON_PRESSED ) {
//            pushbuttonWasPressed = 1;
//        }
//#endif
//
//    }
//}
