/*******************************************************************************
 * @file    ble_data_tx.c
 * @brief   BLE Data Transmission Functions
 * @author  Charles KWON <charleskwon@medithings.co.kr>
 * @date    2025-01-30
 * @copyright (c) 2025 Medithings Inc. All rights reserved.
 *
 * @details BLE NUS data transmission with CRC16 checksum.
 ******************************************************************************/

#include "ble_data_tx.h"
#include "ble_core.h"
#include "ble_nus.h"
#include "crc16.h"
#include "nrf_delay.h"
#include "app_error.h"
#include "debug_print.h"
#include <string.h>

/*==============================================================================
 * GLOBAL VARIABLES
 *============================================================================*/

volatile bool ble_connection_st = 0;
volatile bool data_tx_in_progress = false;

uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN];

/*==============================================================================
 * PRIVATE VARIABLES
 *============================================================================*/

static uint8_t  m_tx_buffer[BLE_NUS_MAX_DATA_LEN];
static uint16_t m_tx_len = 0;
static bool     m_tx_in_progress = false;

/*==============================================================================
 * DATA TRANSMISSION FUNCTIONS
 *============================================================================*/

void data_tx_handler(char const *p_data_to_send)
{
    if (m_tx_in_progress)
    {
        return;
    }

    /* Find string length by locating '\r' terminator */
    char const *p_end_char = strchr(p_data_to_send, '\r');
    if (p_end_char == NULL)
    {
        DBG_PRINTF("TX data has no '\\r' terminator. Aborting send.");
        return;
    }

    uint16_t data_len = p_end_char - p_data_to_send;

    /* Validate length (need space for 2-byte CRC) */
    if (data_len > (BLE_NUS_MAX_DATA_LEN - 2))
    {
        DBG_PRINTF("TX data is too long to fit CRC. Len: %d", data_len);
        return;
    }

    /* Prepare buffer with data and CRC16 */
    memcpy(m_tx_buffer, p_data_to_send, data_len);
    uint16_t crc = crc16_compute(m_tx_buffer, data_len, NULL);
    m_tx_buffer[data_len]     = (uint8_t)(crc & 0xFF);
    m_tx_buffer[data_len + 1] = (uint8_t)((crc >> 8) & 0xFF);

    m_tx_len = data_len + 2;

    /* Attempt to send */
    uint32_t err_code;

    if (ble_connection_st == BLE_CONNECTED_ST)
    {
        m_tx_in_progress = true;

        err_code = ble_nus_data_send_wrapper( m_tx_buffer, &m_tx_len, m_conn_handle);

        if (err_code == NRF_SUCCESS)
        {
            DBG_PRINTF("NUS data sent to SoftDevice buffer.");
        }
        else if (err_code == NRF_ERROR_RESOURCES)
        {
            DBG_PRINTF("NUS buffer full. Will retry later.");
        }
        else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND)
        {
            ble_connection_st = BLE_DISCONNECTED_ST;
            DBG_PRINTF("Failed to send NUS data, device not connected.");
            m_tx_in_progress = false;
        }
        else
        {
            m_tx_in_progress = false;
            APP_ERROR_CHECK(err_code);
        }
    }
}

void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length)
{
    uint32_t err_code;
    static uint8_t tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0};

    if (ble_connection_st == 0) {
        DBG_PRINTF("Lost!\r\n");
        return;
    }

    data_tx_in_progress = true;

    if (length * sizeof(uint16_t) > (BLE_NUS_MAX_DATA_LEN - 2)) {
        return;
    }

    if (ble_connection_st == BLE_CONNECTED_ST) {
        memcpy(tx_buffer, ble_bin_buff, length * sizeof(uint16_t));
        uint16_t crc = crc16_compute(tx_buffer, length * sizeof(uint16_t), NULL);

        tx_buffer[length * sizeof(uint16_t)]     = (uint8_t)(crc & 0xFF);
        tx_buffer[length * sizeof(uint16_t) + 1] = (uint8_t)((crc >> 8) & 0xFF);

        uint16_t total_len = length * sizeof(uint16_t) + 2;

        do {
            err_code = ble_nus_data_send_wrapper( tx_buffer, &total_len, m_conn_handle);

            if ((err_code != NRF_ERROR_INVALID_STATE) &&
                (err_code != NRF_ERROR_RESOURCES) &&
                (err_code != NRF_ERROR_NOT_FOUND))
            {
                nrf_delay_ms(10);
            }

            APP_ERROR_CHECK(err_code);

        } while (err_code == NRF_ERROR_RESOURCES);

        data_tx_in_progress = false;
    }
}

void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length)
{
    binary_tx_handler(ble_bin_buff, length);
}

/*==============================================================================
 * DATA FORMATTING FUNCTIONS
 *============================================================================*/

void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value)
{
    uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
    uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];

    buffer[0] = (uint8_t)(tag1 & 0xFF);
    buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
    buffer[2] = (uint8_t)(tag2 & 0xFF);
    buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);

    buffer[5] = (uint8_t)(value & 0xFF);
    buffer[4] = (uint8_t)((value >> 8) & 0xFF);
}

void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length)
{
    uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
    uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];

    buffer[0] = (uint8_t)(tag1 & 0xFF);
    buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
    buffer[2] = (uint8_t)(tag2 & 0xFF);
    buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);

    for (size_t i = 0; i < length; i++) {
        buffer[4 + i * 2 + 1] = (uint8_t)(data_array[i] & 0xFF);
        buffer[4 + i * 2]     = (uint8_t)((data_array[i] >> 8) & 0xFF);
    }
}

void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length)
{
    uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
    uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];

    buffer[0] = (uint8_t)(tag1 & 0xFF);
    buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
    buffer[2] = (uint8_t)(tag2 & 0xFF);
    buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);

    for (size_t i = 0; i < length; i++) {
        buffer[4 + i] = (uint8_t)(data_array[i] & 0xFF);
    }
}

void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length)
{
    uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
    uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];

    buffer[0] = (uint8_t)(tag1 & 0xFF);
    buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
    buffer[2] = (uint8_t)(tag2 & 0xFF);
    buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);

    for (size_t i = 0; i < length; i++) {
        buffer[4 + i] = (uint8_t)(data_ascii[i] & 0xFF);
    }
}

/*==============================================================================
 * TX COMPLETE HANDLER (called from ble_evt_handler)
 *============================================================================*/

void ble_data_tx_complete(void)
{
    m_tx_in_progress = false;
}
