/*
 * Copyright 2021 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_component_serial_manager.h"
#include "fsl_component_serial_port_internal.h"

#if (defined(SERIAL_PORT_TYPE_SPI) && (SERIAL_PORT_TYPE_SPI > 0U))
#include "fsl_adapter_spi.h"
#include "fsl_component_serial_port_spi.h"
#endif

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#ifndef NDEBUG
#if (defined(DEBUG_CONSOLE_ASSERT_DISABLE) && (DEBUG_CONSOLE_ASSERT_DISABLE > 0U))
#undef assert
#define assert(n)
#else
/* MISRA C-2012 Rule 17.2 */
#undef assert
#define assert(n) \
    while (!(n))  \
    {             \
        ;         \
    }
#endif
#endif

#define SERIAL_PORT_SPI_RECEIVE_DATA_LENGTH 1U
#define SERIAL_MANAGER_BLOCK_OFFSET         (12U)

typedef struct _serial_spi_master_send_state
{
    uint8_t *buffer;
    uint32_t length;
    serial_manager_callback_t callback;
    void *callbackParam;
} serial_spi_master_send_state_t;

typedef struct _serial_spi_master_recv_state
{
    uint8_t *buffer;
    uint32_t length;
    serial_manager_callback_t callback;
    void *callbackParam;
} serial_spi_master_recv_state_t;

typedef struct _serial_spi_master_state
{
    HAL_SPI_MASTER_HANDLE_DEFINE(spiMasterHandleBuffer);
    uint32_t configFlags;
#if (defined(SERIAL_MANAGER_NON_BLOCKING_MODE) && (SERIAL_MANAGER_NON_BLOCKING_MODE > 0U))
    serial_spi_master_send_state_t tx;
    serial_spi_master_recv_state_t rx;
#endif
} serial_spi_master_state_t;

typedef struct _serial_spi_slave_send_state
{
    uint8_t *buffer;
    uint32_t length;
    serial_manager_callback_t callback;
    void *callbackParam;
} serial_spi_slave_send_state_t;

typedef struct _serial_spi_slave_recv_state
{
    uint8_t *buffer;
    uint32_t length;
    serial_manager_callback_t callback;
    void *callbackParam;
} serial_spi_slave_recv_state_t;

typedef struct _serial_spi_slave_state
{
    HAL_SPI_SLAVE_HANDLE_DEFINE(spiSlaveHandleBuffer);
    uint32_t configFlags;
#if (defined(SERIAL_MANAGER_NON_BLOCKING_MODE) && (SERIAL_MANAGER_NON_BLOCKING_MODE > 0U))
    serial_spi_slave_send_state_t tx;
    serial_spi_slave_recv_state_t rx;
#endif
} serial_spi_slave_state_t;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Code
 ******************************************************************************/
#if (defined(SERIAL_MANAGER_NON_BLOCKING_MODE) && (SERIAL_MANAGER_NON_BLOCKING_MODE > 0U))
void Serial_SpiMasterTxCallback(hal_spi_master_handle_t handle, hal_spi_status_t status, void *callbackParam)
{
    serial_spi_master_state_t *serialSpiMasterHandle;
    serial_manager_callback_message_t msg;

    if (NULL != callbackParam)
    {
        serialSpiMasterHandle = (serial_spi_master_state_t *)callbackParam;
        if (NULL != serialSpiMasterHandle->tx.callback)
        {
            serial_manager_status_t serialManagerStatus = kStatus_SerialManager_Success;
            msg.buffer                                  = serialSpiMasterHandle->tx.buffer;
            msg.length                                  = serialSpiMasterHandle->tx.length;
            if (NULL != serialSpiMasterHandle->tx.callback)
            {
                serialSpiMasterHandle->tx.callback(serialSpiMasterHandle->tx.callbackParam, &msg, serialManagerStatus);
            }
        }
    }
}

void Serial_SpiMasterRxCallback(hal_spi_master_handle_t handle, hal_spi_status_t status, void *callbackParam)
{
    serial_spi_master_state_t *serialSpiMasterHandle;
    serial_manager_callback_message_t msg;

    if (NULL != callbackParam)
    {
        serialSpiMasterHandle                       = (serial_spi_master_state_t *)callbackParam;
        serial_manager_status_t serialManagerStatus = kStatus_SerialManager_Success;
        msg.buffer                                  = serialSpiMasterHandle->rx.buffer;
        msg.length                                  = serialSpiMasterHandle->rx.length;
        if (NULL != serialSpiMasterHandle->rx.callback)
        {
            serialSpiMasterHandle->rx.callback(serialSpiMasterHandle->rx.callbackParam, &msg, serialManagerStatus);
        }
    }
}

void Serial_SpiSlaveTxCallback(hal_spi_slave_handle_t handle, hal_spi_status_t status, void *callbackParam)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;
    serial_manager_callback_message_t msg;

    if (NULL != callbackParam)
    {
        serialSpiSlaveHandle                        = (serial_spi_slave_state_t *)callbackParam;
        serial_manager_status_t serialManagerStatus = kStatus_SerialManager_Success;
        msg.buffer                                  = serialSpiSlaveHandle->tx.buffer;
        msg.length                                  = serialSpiSlaveHandle->tx.length;
        if (NULL != serialSpiSlaveHandle->tx.callback)
        {
            serialSpiSlaveHandle->tx.callback(serialSpiSlaveHandle->tx.callbackParam, &msg, serialManagerStatus);
        }
    }
}

void Serial_SpiSlaveRxCallback(hal_spi_slave_handle_t handle, hal_spi_status_t status, void *callbackParam)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;
    serial_manager_callback_message_t msg;
    if (NULL != callbackParam)
    {
        serialSpiSlaveHandle                        = (serial_spi_slave_state_t *)callbackParam;
        serial_manager_status_t serialManagerStatus = kStatus_SerialManager_Success;
        msg.buffer                                  = serialSpiSlaveHandle->rx.buffer;
        msg.length                                  = serialSpiSlaveHandle->rx.length;
        if (NULL != serialSpiSlaveHandle->rx.callback)
        {
            serialSpiSlaveHandle->rx.callback(serialSpiSlaveHandle->rx.callbackParam, &msg, serialManagerStatus);
        }
    }
}
#endif /* SERIAL_MANAGER_NON_BLOCKING_MODE */
serial_manager_status_t Serial_SpiMasterInit(serial_handle_t serialHandle, void *serialConfig)
{
    serial_spi_master_state_t *serialSpiMasterHandle;
    serial_spi_master_config_t *serialSpiMasterConfig;
#if (defined(HAL_SPI_MASTER_DMA_ENABLE) && (HAL_SPI_MASTER_DMA_ENABLE > 0U))
    hal_spi_dma_config_t *spi_dma_config;
#endif

    assert(serialHandle);
    assert(serialConfig);
#if (defined(HAL_SPI_MASTER_DMA_ENABLE) && (HAL_SPI_MASTER_DMA_ENABLE > 0U))
    assert((SERIAL_PORT_SPI_MASTER_HANDLE_SIZE - HAL_SPI_MASTER_DMA_HANDLE_SIZE) >= sizeof(serial_spi_master_state_t));
#else
    assert(SERIAL_PORT_SPI_MASTER_HANDLE_SIZE >= sizeof(serial_spi_master_state_t));
#endif
    serialSpiMasterHandle = (serial_spi_master_state_t *)serialHandle;
    serialSpiMasterConfig = (serial_spi_master_config_t *)serialConfig;

    if (kStatus_HAL_SpiSuccess !=
        HAL_SpiMasterInit((hal_spi_master_handle_t)serialSpiMasterHandle->spiMasterHandleBuffer,
                          (const hal_spi_master_config_t *)serialSpiMasterConfig))
    {
        return kStatus_SerialManager_Error;
    }
    serialSpiMasterHandle->configFlags = serialSpiMasterConfig->configFlags;
#if (defined(HAL_SPI_MASTER_DMA_ENABLE) && (HAL_SPI_MASTER_DMA_ENABLE > 0U))
    if (serialSpiMasterConfig->enableDMA == 1)
    {
        spi_dma_config = (hal_spi_dma_config_t *)serialSpiMasterConfig->dmaConfig;
        HAL_SpiMasterDMAInit((hal_spi_master_handle_t)serialSpiMasterHandle->spiMasterHandleBuffer,
                             (uint8_t *)serialSpiMasterHandle->spiMasterHandleBuffer +
                                 (SERIAL_PORT_SPI_MASTER_HANDLE_SIZE - HAL_SPI_MASTER_DMA_HANDLE_SIZE),
                             spi_dma_config);
    }

#endif

#if (defined(SERIAL_MANAGER_NON_BLOCKING_MODE) && (SERIAL_MANAGER_NON_BLOCKING_MODE > 0U))
#endif /* SERIAL_MANAGER_NON_BLOCKING_MODE */

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiMasterDeinit(serial_handle_t serialHandle)
{
    serial_spi_master_state_t *serialSpiMasterHandle;

    assert(serialHandle);

    serialSpiMasterHandle = (serial_spi_master_state_t *)serialHandle;

    (void)HAL_SpiMasterDeinit((hal_spi_master_handle_t)serialSpiMasterHandle->spiMasterHandleBuffer);

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiSlaveInit(serial_handle_t serialHandle, void *serialConfig)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;
    serial_spi_slave_config_t *serialSpiSlaveConfig;
#if (defined(HAL_SPI_SLAVE_DMA_ENABLE) && (HAL_SPI_SLAVE_DMA_ENABLE > 0U))
    hal_spi_dma_config_t *spi_dma_config;
#endif

    assert(serialHandle);
    assert(serialConfig);

#if (defined(HAL_SPI_SLAVE_DMA_ENABLE) && (HAL_SPI_SLAVE_DMA_ENABLE > 0U))
    assert((SERIAL_PORT_SPI_SLAVE_HANDLE_SIZE - HAL_SPI_SLAVE_DMA_HANDLE_SIZE) >= sizeof(serial_spi_slave_state_t));
#else
    assert(SERIAL_PORT_SPI_SLAVE_HANDLE_SIZE >= sizeof(serial_spi_slave_state_t));
#endif

    serialSpiSlaveHandle = (serial_spi_slave_state_t *)serialHandle;
    serialSpiSlaveConfig = (serial_spi_slave_config_t *)serialConfig;

    if (kStatus_HAL_SpiSuccess != HAL_SpiSlaveInit((hal_spi_slave_handle_t)serialSpiSlaveHandle->spiSlaveHandleBuffer,
                                                   (hal_spi_slave_config_t *)serialSpiSlaveConfig))
    {
        return kStatus_SerialManager_Error;
    }
    serialSpiSlaveHandle->configFlags = serialSpiSlaveConfig->configFlags;
#if (defined(HAL_SPI_SLAVE_DMA_ENABLE) && (HAL_SPI_SLAVE_DMA_ENABLE > 0U))
    if (serialSpiSlaveConfig->enableDMA == 1)
    {
        spi_dma_config = (hal_spi_dma_config_t *)serialSpiSlaveConfig->dmaConfig;
        HAL_SpiSlaveDMAInit((hal_spi_slave_handle_t)serialSpiSlaveHandle->spiSlaveHandleBuffer,
                            (uint8_t *)serialSpiSlaveHandle->spiSlaveHandleBuffer +
                                (SERIAL_PORT_SPI_SLAVE_HANDLE_SIZE - HAL_SPI_SLAVE_DMA_HANDLE_SIZE),
                            spi_dma_config);
    }

#endif

#if (defined(SERIAL_MANAGER_NON_BLOCKING_MODE) && (SERIAL_MANAGER_NON_BLOCKING_MODE > 0U))
#endif /* SERIAL_MANAGER_NON_BLOCKING_MODE */

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiSlaveDeinit(serial_handle_t serialHandle)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;

    assert(serialHandle);

    serialSpiSlaveHandle = (serial_spi_slave_state_t *)serialHandle;

    (void)HAL_SpiSlaveDeinit((hal_spi_slave_handle_t)serialSpiSlaveHandle->spiSlaveHandleBuffer);

    return kStatus_SerialManager_Success;
}

#if (defined(SERIAL_MANAGER_NON_BLOCKING_MODE) && (SERIAL_MANAGER_NON_BLOCKING_MODE > 0U))

serial_manager_status_t Serial_SpiMasterWrite(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_master_state_t *serialSpiMasterHandle;
    hal_spi_transfer_t mastetTransfer;

    assert(serialHandle);
    assert(buffer);
    assert(length);
    serialSpiMasterHandle            = (serial_spi_master_state_t *)serialHandle;
    serialSpiMasterHandle->tx.buffer = buffer;
    serialSpiMasterHandle->tx.length = (size_t)length;

    if (kStatus_HAL_SpiSuccess !=
        HAL_SpiMasterTransferInstallCallback((hal_spi_master_handle_t)serialSpiMasterHandle->spiMasterHandleBuffer,
                                             Serial_SpiMasterTxCallback, serialHandle))
    {
        return kStatus_SerialManager_Error;
    }
    serialSpiMasterHandle   = (serial_spi_master_state_t *)serialHandle;
    mastetTransfer.txData   = buffer;
    mastetTransfer.rxData   = NULL;
    mastetTransfer.dataSize = (size_t)length;
    mastetTransfer.flags    = serialSpiMasterHandle->configFlags;

    return (serial_manager_status_t)HAL_SpiMasterTransferNonBlocking(
        ((hal_spi_master_handle_t)&serialSpiMasterHandle->spiMasterHandleBuffer[0]), &mastetTransfer);
}

serial_manager_status_t Serial_SpiSlaveWrite(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;
    hal_spi_transfer_t slaveTransfer;
    assert(serialHandle);
    assert(buffer);
    assert(length);

    serialSpiSlaveHandle            = (serial_spi_slave_state_t *)serialHandle;
    serialSpiSlaveHandle->tx.buffer = buffer;
    serialSpiSlaveHandle->tx.length = (size_t)length;

    if (kStatus_HAL_SpiSuccess !=
        HAL_SpiSlaveTransferInstallCallback((hal_spi_slave_handle_t)serialSpiSlaveHandle->spiSlaveHandleBuffer,
                                            Serial_SpiSlaveTxCallback, serialHandle))
    {
        return kStatus_SerialManager_Error;
    }
    slaveTransfer.txData   = buffer;
    slaveTransfer.rxData   = NULL;
    slaveTransfer.dataSize = (size_t)length;
    slaveTransfer.flags    = serialSpiSlaveHandle->configFlags;
    return (serial_manager_status_t)HAL_SpiSlaveTransferNonBlocking(
        ((hal_spi_slave_handle_t)&serialSpiSlaveHandle->spiSlaveHandleBuffer[0]), &slaveTransfer);
}

// read Serial_SpiMasterRead
serial_manager_status_t Serial_SpiMasterRead(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_master_state_t *serialSpiMasterHandle;
    hal_spi_transfer_t masterReceiver;

    assert(serialHandle);
    assert(buffer);
    assert(length);

    serialSpiMasterHandle = (serial_spi_master_state_t *)serialHandle;

    serialSpiMasterHandle->rx.buffer = buffer;
    serialSpiMasterHandle->rx.length = (size_t)length;
    if (kStatus_HAL_SpiSuccess !=
        HAL_SpiMasterTransferInstallCallback((hal_spi_master_handle_t)serialSpiMasterHandle->spiMasterHandleBuffer,
                                             Serial_SpiMasterRxCallback, serialHandle))
    {
        return kStatus_SerialManager_Error;
    }

    masterReceiver.txData   = NULL;
    masterReceiver.rxData   = buffer;
    masterReceiver.dataSize = (size_t)length;
    masterReceiver.flags    = serialSpiMasterHandle->configFlags;
    return (serial_manager_status_t)HAL_SpiMasterTransferNonBlocking(
        ((hal_spi_master_handle_t)&serialSpiMasterHandle->spiMasterHandleBuffer[0]), &masterReceiver);
}

serial_manager_status_t Serial_SpiSlaveRead(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;
    hal_spi_transfer_t slaveReceiver;

    assert(serialHandle);
    assert(buffer);
    assert(length);

    serialSpiSlaveHandle            = (serial_spi_slave_state_t *)serialHandle;
    serialSpiSlaveHandle->rx.buffer = buffer;
    serialSpiSlaveHandle->rx.length = (size_t)length;

    if (kStatus_HAL_SpiSuccess !=
        HAL_SpiSlaveTransferInstallCallback((hal_spi_slave_handle_t)serialSpiSlaveHandle->spiSlaveHandleBuffer,
                                            Serial_SpiSlaveRxCallback, serialHandle))
    {
        return kStatus_SerialManager_Error;
    }
    slaveReceiver.txData   = NULL;
    slaveReceiver.rxData   = buffer;
    slaveReceiver.dataSize = (size_t)length;
    slaveReceiver.flags    = serialSpiSlaveHandle->configFlags;
    return (serial_manager_status_t)HAL_SpiSlaveTransferNonBlocking(
        ((hal_spi_slave_handle_t)&serialSpiSlaveHandle->spiSlaveHandleBuffer[0]), &slaveReceiver);
}

#else /* SERIAL_MANAGER_NON_BLOCKING_DUAL_MODE */

serial_manager_status_t Serial_SpiMasterWrite(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_master_state_t *serialSpiMasterHandle;
    hal_spi_transfer_t mastetTransfer;

    assert(serialHandle);
    assert(buffer);
    assert(length);

    serialSpiMasterHandle = (serial_spi_master_state_t *)serialHandle;

    mastetTransfer.txData = buffer;
    mastetTransfer.rxData = NULL;
    mastetTransfer.dataSize = (size_t)length;
    return (serial_manager_status_t)HAL_SpiMasterTransferBlocking(
        ((hal_spi_master_handle_t)&serialSpiMasterHandle->spiMasterHandleBuffer[0]), &mastetTransfer);
}

serial_manager_status_t Serial_SpiMasterRead(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_master_state_t *serialSpiMasterHandle;
    hal_spi_transfer_t masterReceiver;

    assert(serialHandle);
    assert(buffer);
    assert(length);

    serialSpiMasterHandle = (serial_spi_master_state_t *)serialHandle;
    masterReceiver.txData = NULL;
    masterReceiver.rxData = buffer;
    masterReceiver.dataSize = (size_t)length;
    return (serial_manager_status_t)HAL_SpiMasterTransferBlocking(
        ((hal_spi_master_handle_t)&serialSpiMasterHandle->spiMasterHandleBuffer[0]), &masterReceiver);
}
#if 0
serial_manager_status_t Serial_SpiSlaveWrite(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_master_state_t *serialSpiSlaveHandle;
    hal_spi_transfer_t slaveTransfer;

    assert(serialHandle);
    assert(buffer);
    assert(length);

    serialSpiSlaveHandle      = (serial_spi_master_state_t *)serialHandle;

    slaveTransfer.txData   = buffer;
    slaveTransfer.rxData   = NULL;
    slaveTransfer.dataSize = (size_t)length;
    return (serial_manager_status_t)HAL_SpiSlaveTransferBlocking(
        ((hal_spi_master_handle_t)&serialSpiSlaveHandle->spiSlaveHandleBuffer[0]), &slaveTransfer);
}

serial_manager_status_t Serial_SpiSlaveRead(serial_handle_t serialHandle, uint8_t *buffer, uint32_t length)
{
    serial_spi_master_state_t *serialSpiSlaveHandle;
    hal_spi_transfer_t slaveReceiver;

    assert(serialHandle);
    assert(buffer);
    assert(length);

    serialSpiSlaveHandle    = (serial_spi_master_state_t *)serialHandle;
    slaveReceiver.txData   = NULL;
    slaveReceiver.rxData   = buffer;
    slaveReceiver.dataSize = (size_t)length;
    return (serial_manager_status_t)HAL_SpiSlaveTransferBlocking(
        ((hal_spi_master_handle_t)&serialSpiSlaveHandle->spiSlaveHandleBuffer[0]), &slaveReceiver);
}
#endif
#endif

#if (defined(SERIAL_MANAGER_NON_BLOCKING_MODE) && (SERIAL_MANAGER_NON_BLOCKING_MODE > 0U))

serial_manager_status_t Serial_SpiMasterInstallTxCallback(serial_handle_t serialHandle,
                                                          serial_manager_callback_t callback,
                                                          void *callbackParam)
{
    serial_spi_master_state_t *serialSpiMasterHandle;

    assert(serialHandle);

    serialSpiMasterHandle = (serial_spi_master_state_t *)serialHandle;

    serialSpiMasterHandle->tx.callback      = callback;
    serialSpiMasterHandle->tx.callbackParam = callbackParam;

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiMasterInstallRxCallback(serial_handle_t serialHandle,
                                                          serial_manager_callback_t callback,
                                                          void *callbackParam)
{
    serial_spi_master_state_t *serialSpiMasterHandle;

    assert(serialHandle);

    serialSpiMasterHandle = (serial_spi_master_state_t *)serialHandle;

    serialSpiMasterHandle->rx.callback      = callback;
    serialSpiMasterHandle->rx.callbackParam = callbackParam;

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiSlaveInstallTxCallback(serial_handle_t serialHandle,
                                                         serial_manager_callback_t callback,
                                                         void *callbackParam)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;

    assert(serialHandle);

    serialSpiSlaveHandle = (serial_spi_slave_state_t *)serialHandle;

    serialSpiSlaveHandle->tx.callback      = callback;
    serialSpiSlaveHandle->tx.callbackParam = callbackParam;

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiSlaveInstallRxCallback(serial_handle_t serialHandle,
                                                         serial_manager_callback_t callback,
                                                         void *callbackParam)
{
    serial_spi_slave_state_t *serialSpiSlaveHandle;

    assert(serialHandle);

    serialSpiSlaveHandle = (serial_spi_slave_state_t *)serialHandle;

    serialSpiSlaveHandle->rx.callback      = callback;
    serialSpiSlaveHandle->rx.callbackParam = callbackParam;

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiMasterCancelWrite(serial_handle_t serialHandle)
{
    serial_spi_master_state_t *serialSpiMaster;

    assert(serialHandle);

    serialSpiMaster = (serial_spi_master_state_t *)serialHandle;

    if (kStatus_HAL_SpiSuccess !=
        HAL_SpiMasterTransferAbort((hal_spi_master_handle_t)serialSpiMaster->spiMasterHandleBuffer))

    {
        return kStatus_SerialManager_Error;
    }

    return kStatus_SerialManager_Success;
}

serial_manager_status_t Serial_SpiSlaveCancelWrite(serial_handle_t serialHandle)
{
    serial_spi_slave_state_t *serialSpiSlave;

    assert(serialHandle);

    serialSpiSlave = (serial_spi_slave_state_t *)serialHandle;

    if (kStatus_HAL_SpiSuccess !=
        HAL_SpiSlaveTransferAbort((hal_spi_slave_handle_t)serialSpiSlave->spiSlaveHandleBuffer))

    {
        return kStatus_SerialManager_Error;
    }

    return kStatus_SerialManager_Success;
}

#endif
