/*******************************************************************************
 * @file    spi2_bus.c
 * @brief   Shared SPI2 Bus - nrfx_spim driver (SoftDevice safe)
 * @author  Charles KWON <charleskwon@medithings.co.kr>
 * @date    2025-02-03
 *
 * @note    Uses nrfx_spim with APP_IRQ_PRIORITY_LOWEST for SoftDevice compatibility
 ******************************************************************************/

#include "spi2_bus.h"
#include "nrfx_spim.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "app_error.h"
#include "app_util_platform.h"  /* For APP_IRQ_PRIORITY_LOWEST */
#include "debug_print.h"
#include <stdio.h>

/* Define instance index if not provided by nrfx (SDK version dependent) */
#ifndef NRFX_SPIM2_INST_IDX
#define NRFX_SPIM2_INST_IDX 0
#endif

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

static bool m_initialized = false;
static nrfx_spim_t m_spi = NRFX_SPIM_INSTANCE(SPI2_BUS_INSTANCE);

/*==============================================================================
 * PUBLIC FUNCTIONS
 *============================================================================*/

ret_code_t spi2_bus_init(void)
{
    if (m_initialized)
    {
        return NRF_SUCCESS;
    }

    DBG_PRINTF("[SPI2] nrfx init...\r\n");

    nrfx_spim_config_t cfg = NRFX_SPIM_DEFAULT_CONFIG;
    cfg.sck_pin      = SPI2_SCK_PIN;
    cfg.mosi_pin     = SPI2_MOSI_PIN;
    cfg.miso_pin     = SPI2_MISO_PIN;
    cfg.ss_pin       = NRFX_SPIM_PIN_NOT_USED;  /* Manual CS control */
    cfg.frequency    = NRF_SPIM_FREQ_1M;  /* Try slower for debugging */
    cfg.mode         = NRF_SPIM_MODE_0;
    cfg.bit_order    = NRF_SPIM_BIT_ORDER_MSB_FIRST;
    cfg.irq_priority = APP_IRQ_PRIORITY_LOWEST;  /* SoftDevice safe! */

    /* Use NULL handler for blocking mode (no IRQ needed) */
    nrfx_err_t err = nrfx_spim_init(&m_spi, &cfg, NULL, NULL);
    DBG_PRINTF("[SPI2] init err=%d (blocking)\r\n", err);

    if (err != NRFX_SUCCESS && err != NRFX_ERROR_INVALID_STATE) {
        DBG_PRINTF("[SPI2] FAIL!\r\n");
        return err;
    }

    /* Configure CS pins as outputs, deasserted (high) */
    nrf_gpio_cfg_output(SPI2_CS_ADA2200);
    nrf_gpio_cfg_output(SPI2_CS_FLASH);
    nrf_gpio_pin_set(SPI2_CS_ADA2200);
    nrf_gpio_pin_set(SPI2_CS_FLASH);

    m_initialized = true;
    DBG_PRINTF("[SPI2] OK\r\n");

    return NRF_SUCCESS;
}

void spi2_bus_uninit(void)
{
    if (!m_initialized) return;

    nrfx_spim_uninit(&m_spi);

    nrf_gpio_cfg_default(SPI2_SCK_PIN);
    nrf_gpio_cfg_default(SPI2_MISO_PIN);
    nrf_gpio_cfg_default(SPI2_MOSI_PIN);
    nrf_gpio_cfg_default(SPI2_CS_ADA2200);
    nrf_gpio_cfg_default(SPI2_CS_FLASH);

    m_initialized = false;
}

bool spi2_bus_is_initialized(void)
{
    return m_initialized;
}

const nrf_drv_spi_t* spi2_bus_get_instance(void)
{
    /* Using nrfx_spim now - return NULL for legacy compatibility */
    return NULL;
}

/* Debug: count transfers for selective logging */
static uint32_t s_xfer_count = 0;

ret_code_t spi2_bus_transfer(const uint8_t *tx_buf, uint8_t tx_len,
                              uint8_t *rx_buf, uint8_t rx_len)
{
    if (!m_initialized)
    {
        DBG_PRINTF("[SPI2] ERROR: not init!\r\n");
        return NRF_ERROR_INVALID_STATE;
    }

    s_xfer_count++;
    bool debug = (s_xfer_count <= 10);

    nrfx_spim_xfer_desc_t xfer;
    nrfx_err_t err;

    /* Blocking mode: nrfx_spim_xfer() waits for completion internally */

    /* Full-duplex mode: TX and RX simultaneously */
    if (tx_len > 0 && rx_len > 0) {
        DBG_PRINTF("[SPI2] FD TX[%d] RX[%d]\r\n", tx_len, rx_len);

        xfer.p_tx_buffer = tx_buf;
        xfer.tx_length   = tx_len;
        xfer.p_rx_buffer = rx_buf;
        xfer.rx_length   = rx_len;

        DBG_PRINTF("[SPI2] xfer...\r\n");
        err = nrfx_spim_xfer(&m_spi, &xfer, 0);
        DBG_PRINTF("[SPI2] xfer done err=%d\r\n", err);
        if (err != NRFX_SUCCESS) {
            DBG_PRINTF("[SPI2] FD err=%d\r\n", err);
            return err;
        }

        DBG_PRINTF("[SPI2] RX: %02X %02X %02X %02X\r\n",
                   rx_buf[0],
                   rx_len > 1 ? rx_buf[1] : 0,
                   rx_len > 2 ? rx_buf[2] : 0,
                   rx_len > 3 ? rx_buf[3] : 0);
    }
    /* TX only mode */
    else if (tx_len > 0) {
        if (debug) {
            DBG_PRINTF("[SPI2] TX[%d]: %02X\r\n", tx_len, tx_buf[0]);
        }

        xfer.p_tx_buffer = tx_buf;
        xfer.tx_length   = tx_len;
        xfer.p_rx_buffer = NULL;
        xfer.rx_length   = 0;

        err = nrfx_spim_xfer(&m_spi, &xfer, 0);
        if (err != NRFX_SUCCESS) {
            DBG_PRINTF("[SPI2] TX err=%d\r\n", err);
            return err;
        }
    }
    /* RX only mode */
    else if (rx_len > 0) {
        xfer.p_tx_buffer = NULL;
        xfer.tx_length   = 0;
        xfer.p_rx_buffer = rx_buf;
        xfer.rx_length   = rx_len;

        err = nrfx_spim_xfer(&m_spi, &xfer, 0);
        if (err != NRFX_SUCCESS) {
            DBG_PRINTF("[SPI2] RX err=%d\r\n", err);
            return err;
        }

        if (debug) {
            DBG_PRINTF("[SPI2] RX[%d]: %02X %02X %02X\r\n",
                       rx_len, rx_buf[0],
                       rx_len > 1 ? rx_buf[1] : 0,
                       rx_len > 2 ? rx_buf[2] : 0);
        }
    }

    return NRF_SUCCESS;
}
