
/*
 * Copyright (c) 2025, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 *  ======== DAP.c ========
 */

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>

#include <ti/ai/edge_ai/dap/DAP.h>
#include <ti/ai/edge_ai/dap/core/DAP_core.h>
#include <ti/ai/edge_ai/dap/interface/DAP_interface.h>

#include <ti/drivers/UART2.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/TaskP.h>
#include <ti/drivers/dpl/MessageQueueP.h>

#define CONFIG_UART2_0 0
extern const uint32_t dapBaudRate;

#define TASK_STACK_SIZE_BYTES 1024
#define TASK_STACK_SIZE_WORDS (TASK_STACK_SIZE_BYTES >> 2)
#define TASK_PRIORITY         4

#define MESSAGE_QUEUE_MAX_ITEMS 4

static DAP_Handle dap;

/* Static globals */
static bool isInitialized = false;

static TaskP_Struct dapRxTask;
static TaskP_Struct dapTxTask;
static uint32_t DAP_rxTaskStack[TASK_STACK_SIZE_WORDS];
static uint32_t DAP_txTaskStack[TASK_STACK_SIZE_WORDS];

static MessageQueueP_Struct dapMessageQueue;
static DAP_Frame messageQueueBuffer[MESSAGE_QUEUE_MAX_ITEMS];

/* Forward declarations */
static void DAP_rxTaskFxn(void *arg);
static void DAP_txTaskFxn(void *arg);

float DAP_swapEndianness(float value)
{
    /* Use a union to access the same bytes as both floats and uint32_t */
    union
    {
        float f;
        uint32_t i;
    } u;

    u.f = value;

    /* Swap byte order of the 32-bit integer */
    u.i = ((u.i & 0x000000FFU) << 24) | ((u.i & 0x0000FF00U) << 8) | ((u.i & 0x00FF0000U) >> 8) | ((u.i & 0xFF000000U) >> 24);

    return u.f;
}

void DAP_init(void)
{
    if (!isInitialized)
    {
        isInitialized = true;
    }
}

void DAP_open(void)
{
    uintptr_t key;
    UART2_Handle uart;
    UART2_Params uartParams;
    MessageQueueP_Handle messageQueue;
    TaskP_Params taskParams;

    key = HwiP_disable();

    if (!isInitialized)
    {
        HwiP_restore(key);
        return;
    }
    HwiP_restore(key);

    dap.isCrc               = false;
    dap.startStreamingFlag  = false;
    dap.pipelineConfig.mode = DAP_PIPELINE_MODE_UNINITIALIZED;

    /* Initialize UART */
    UART2_Params_init(&uartParams);
    uartParams.readMode       = UART2_Mode_BLOCKING;
    uartParams.writeMode      = UART2_Mode_BLOCKING;
    uartParams.readReturnMode = UART2_ReadReturnMode_PARTIAL;
    uartParams.baudRate       = dapBaudRate;

    uart = UART2_open(CONFIG_UART2_0, &uartParams);

    dap.uartHandle = uart;

    /* Initialize message queue */
    messageQueue           = MessageQueueP_construct(&dapMessageQueue,
                                           sizeof(DAP_Frame),
                                           MESSAGE_QUEUE_MAX_ITEMS,
                                           messageQueueBuffer);
    dap.messageQueueHandle = messageQueue;

    /* Initialize DAP RX task */
    TaskP_Params_init(&taskParams);
    taskParams.name      = "DAPRX";
    taskParams.arg       = (void *)&dap;
    taskParams.priority  = TASK_PRIORITY;
    taskParams.stackSize = TASK_STACK_SIZE_BYTES;
    taskParams.stack     = DAP_rxTaskStack;

    TaskP_construct(&dapRxTask, DAP_rxTaskFxn, &taskParams);

    /* Initialize DAP TX task */
    taskParams.name      = "DAPTX";
    taskParams.arg       = (void *)&dap;
    taskParams.priority  = TASK_PRIORITY;
    taskParams.stackSize = TASK_STACK_SIZE_BYTES;
    taskParams.stack     = DAP_txTaskStack;

    TaskP_construct(&dapTxTask, DAP_txTaskFxn, &taskParams);
}

DAP_PipelineConfiguration DAP_getPipelineConfiguration(void)
{
    return dap.pipelineConfig;
}

uint32_t DAP_getSamplesToSend(void)
{
    return properties[0].value.u32;
}

uint32_t DAP_getSamplingFrequency(void)
{
    return properties[1].value.u32;
}

bool DAP_isReadyToStartStreaming(void)
{
    return dap.startStreamingFlag;
}

void DAP_sendData(DAP_SendDataType sendDataType, void *data, size_t dataSize)
{
    DAP_receiveDataCommand(&dap, sendDataType, data, dataSize);
}

/*
 *  ======== DAP_rxTaskFxn ========
 */
static void DAP_rxTaskFxn(void *arg)
{
    DAP_Handle *dapHandle   = (DAP_Handle *)arg;
    UART2_Handle uartHandle = dapHandle->uartHandle;

    DAP_Frame receivedFrame;
    DAP_ErrorType dapError;

    while (1)
    {
        UART2_read(uartHandle, receivedFrame.buffer, (size_t)DAP_MAX_FRAME_SIZE_BYTES, &(receivedFrame.length));

        dapError = DAP_verifyFrame(&receivedFrame);

        if (dapError == DAP_ERROR_TYPE_NONE)
        {
            /* Act on the received command and send and "ack" back to the host. */
            DAP_serviceCommand(dapHandle, &receivedFrame);
        }
        else if (dapError == DAP_ERROR_TYPE_MISMATCH_CRC)
        {
            DAP_sendError(dapHandle, DAP_ERROR_COMMAND, "CRC MISMATCH");
        }
    }
}

/*
 *  ======== DAP_txTaskFxn ========
 */
static void DAP_txTaskFxn(void *arg)
{
    DAP_Handle *dapHandle                   = (DAP_Handle *)arg;
    UART2_Handle uartHandle                 = dapHandle->uartHandle;
    MessageQueueP_Handle messageQueueHandle = dapHandle->messageQueueHandle;

    DAP_Frame transmitFrame;

    while (1)
    {
        MessageQueueP_pend(messageQueueHandle, (void *)(&transmitFrame), MessageQueueP_WAIT_FOREVER);
        UART2_write(uartHandle, transmitFrame.buffer, transmitFrame.length, NULL);
    }
}
