/**
 * @file        gps.c
 */

/* Includes *******************************************************************/
#include <stdint.h>
#include <stdbool.h>

#include "gps.h"

extern uint16_t uiGPSTimer;

/* Constants ******************************************************************/

/* Private variables **********************************************************/
static uint8_t gps_rx[GPS_LEN_MAX];
static uint8_t gps_flags = 0;

static gps_stamp_t old_stamp;
static gps_stamp_t new_stamp;
#define MSB16(a) ((uint8_t)(((a) & 0xFF00) >> 8))
#define LSB16(a) ((uint8_t)(((a) & 0x00FF) >> 0))

/* Private functions **********************************************************/

/**
 * @brief   GPSi_Decode()
 *          Decodes the message header from the read gps_buff.
 */
uint8_t GPSi_Decode(uint8_t* buffer, gps_stamp_t* msg);

void GPSi_Encode(uint8_t* data);
/**
 * @brief   GPSi_FlushRX()
 *          Clears the internal buffer.
 */
void GPSi_FlushRX(void);
void GPSi_DMA_Read(void);

/*============================================================================*/
/* Implementation *************************************************************/
/*============================================================================*/

void GPS_GetStamp(uint8_t* stampData)
{
    uint8_t tmpData[24];
    GPSi_Encode(tmpData);
    for (int i = 0; i<24; i++) {
        *(stampData+i) = tmpData[i];
    }
}

gps_stamp_t GPS_GetTimeStamp(void)
{
    return old_stamp;
}
void GPSi_Encode(uint8_t* data) {
    data[0] = 85;
    data[1] = 84;
    data[2] = 67;

    data[3] = 32;

    data[4] = MSB16(old_stamp.Year);
    data[5] = LSB16(old_stamp.Year);

    data[6] = 46;

    data[7] = MSB16(old_stamp.Month);
    data[8] = LSB16(old_stamp.Month);

    data[9] = 46;

    data[10] = MSB16(old_stamp.Day);
    data[11] = LSB16(old_stamp.Day);

    data[12] = 32;

    data[13] = MSB16(old_stamp.Hour);
    data[14] = LSB16(old_stamp.Hour);

    data[15] = 58;

    data[16] = MSB16(old_stamp.Minute);
    data[17] = LSB16(old_stamp.Minute);

    data[18] = 58;

    data[19] = MSB16(old_stamp.Second);
    data[20] = LSB16(old_stamp.Second);

    data[21] = 32;
    data[22] = old_stamp.ParamA;
    data[23] = old_stamp.ParamB;

}
 void GPS_Handler(void)
{
    /* PPS arrived, start DMA read */
    if (GPS_FLAG_PPS == gps_flags) {
        gps_flags &= ~GPS_FLAG_PPS;
        GPSi_DMA_Read();
    }

    /* DMA Read complete, update data */
    if (GPS_FLAG_RX == gps_flags) {
        gps_flags &= ~GPS_FLAG_RX;
        GPS_NewStamp();
    }

    /* Reset DMA if timeout */
    if (0 == uiGPSTimer) {
        GPS_Error();
    }
}

void GPS_Update(void)
{
    old_stamp = new_stamp;
}

void GPS_NewStamp(void)
{
    gps_stamp_t received_stamp;
    uint8_t retval = 0;
    retval = GPSi_Decode(gps_rx, &received_stamp);
    if (1 == retval) {
        new_stamp = received_stamp;
    }
}

uint8_t GPSi_Decode(uint8_t* buffer, gps_stamp_t* msg)
{
    uint8_t retval = 0;
    if ((buffer[0] == 85) && (buffer[1] == 84) && (buffer[2] == 67)) {
        /* Read stamp */
        msg->Year   = (uint16_t)(10*(buffer[4] - 48) + (buffer[5] - 48));
        msg->Month  = (uint8_t)(10*(buffer[7] - 48) + (buffer[8] - 48));
        msg->Day    = (uint16_t)(10*(buffer[10] - 48) + (buffer[11] - 48));
        msg->Hour   = (uint8_t)(10*(buffer[13] - 48) + (buffer[14] - 48));
        msg->Minute = (uint8_t)(10*(buffer[16] - 48) + (buffer[17] - 48));
        msg->Second = (uint8_t)(10*(buffer[19] - 48) + (buffer[20] - 48));
        msg->ParamA = buffer[22];
        msg->ParamB = buffer[23];
        retval = 1;
    }
    return retval;
}

 void GPSi_FlushRX(void)
{
    for(int16_t i = 0u; i < GPS_LEN_MAX; i++) {
        gps_rx[i] = 0u;
    }
}

/* Public functions ***********************************************************/

void GPS_Init(void)
{
     GPSi_DMA_Read();
}

void GPSi_DMA_Read(void)
{
     GPSi_FlushRX();
     HAL_UART_Receive_DMA(GPS_UART, gps_rx, GPS_LEN_STAMP);
     uiGPSTimer = GPS_TIMEOUT;
}

void GPS_Error(void)
{
    HAL_UART_AbortReceive(GPS_UART);
    GPSi_FlushRX();
    HAL_UART_Receive_DMA(GPS_UART, gps_rx, GPS_LEN_STAMP);
    uiGPSTimer = GPS_TIMEOUT;
}

void GPS_RaiseFlag(uint16_t flag)
{
    gps_flags |= flag;
}

