/*
 * Copyright 2018, NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include <string.h>
#include <assert.h>
#include <string.h>

#include "fsl_common.h"

#include "srtm_heap.h"
#include "srtm_list.h"
#include "srtm_dispatcher.h"
#include "srtm_service.h"
#include "srtm_service_struct.h"
#include "srtm_i2c_service.h"
#include "srtm_message.h"
#include "srtm_message_struct.h"
#include "srtm_channel.h"
#include "srtm_channel_struct.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* Protocol definition */
#define SRTM_I2C_CATEGORY (0x9U)
#define SRTM_I2C_VERSION  (0x0100U)
// #define SRTM_I2C_VERSION (0x0001U)

#define SRTM_I2C_RETURN_CODE_SUCEESS     (0x0U)
#define SRTM_I2C_RETURN_CODE_FAIL        (0x1U)
#define SRTM_I2C_RETURN_CODE_UNSUPPORTED (0x2U)

typedef struct _srtm_i2c_service
{
    struct _srtm_service service;
    srtm_i2c_adapter_t adapter;
} * srtm_i2c_service_t;

static srtm_status_t SRTM_I2CService_Request(srtm_service_t service, srtm_request_t request);
static srtm_status_t SRTM_I2CService_Notify(srtm_service_t service, srtm_notification_t notif);

static i2c_bus_t SRTM_I2C_SearchBus(srtm_i2c_adapter_t adapter, uint8_t busID)
{
    uint8_t bus_num    = adapter->bus_structure.bus_num;
    i2c_bus_t busArray = adapter->bus_structure.buses;
    uint8_t i;

    for (i = 0; i != bus_num; i++)
    {
        if (busArray[i].bus_id == busID)
            break;
    }

    return (i == bus_num) ? NULL : (busArray + i);
}

static srtm_status_t SRTM_I2CService_ReadBus(
    srtm_service_t service, uint8_t busID, uint16_t slaveAddr, uint8_t *buf, uint8_t len, uint16_t flags)
{
    srtm_i2c_service_t handle  = (srtm_i2c_service_t)service;
    srtm_i2c_adapter_t adapter = handle->adapter;
    i2c_bus_t targetBus;
    uint32_t base_addr;
    uint8_t switch_index;
    uint16_t switch_addr;
    srtm_i2c_switch_channel switch_channel;
    srtm_status_t status;
    srtm_i2c_type_t type;
    i2c_switch_t switch_inst;

    targetBus    = SRTM_I2C_SearchBus(adapter, busID);
    base_addr    = targetBus->base_addr;
    switch_index = targetBus->switch_idx;
    type         = targetBus->type;
    /*
     * Switch Channel
     */
    if (switch_index < adapter->bus_structure.switch_num)
    {
        switch_inst    = &adapter->bus_structure.switches[switch_index];
        switch_addr    = switch_inst->slaveAddr;
        switch_channel = targetBus->switch_channel;
        if (switch_inst->cur_channel != switch_channel)
        {
            (void)adapter->switchchannel(adapter, base_addr, type, switch_addr, switch_channel);
            switch_inst->cur_channel = switch_channel;
        }
    }
    /*
     * Read
     */
    status = adapter->read(adapter, base_addr, type, slaveAddr, buf, len, flags); // APP_SRTM_I2C_Read
    return status;
}

static srtm_status_t SRTM_I2CService_WriteBus(
    srtm_service_t service, uint8_t busID, uint16_t slaveAddr, uint8_t *buf, uint8_t len, uint16_t flags)
{
    srtm_i2c_service_t handle  = (srtm_i2c_service_t)service;
    srtm_i2c_adapter_t adapter = handle->adapter;
    i2c_bus_t targetBus;
    uint32_t base_addr;
    uint8_t switch_index;
    uint16_t switch_addr;
    srtm_i2c_switch_channel switch_channel;
    srtm_status_t status;
    srtm_i2c_type_t type;
    i2c_switch_t switch_inst;

    targetBus    = SRTM_I2C_SearchBus(adapter, busID);
    base_addr    = targetBus->base_addr;
    switch_index = targetBus->switch_idx;
    type         = targetBus->type;
    /*
     * Switch Channel
     */
    if (switch_index < adapter->bus_structure.switch_num)
    {
        switch_inst    = &adapter->bus_structure.switches[switch_index];
        switch_addr    = switch_inst->slaveAddr;
        switch_channel = targetBus->switch_channel;
        if (switch_inst->cur_channel != switch_channel)
        {
            (void)adapter->switchchannel(adapter, base_addr, type, switch_addr, switch_channel);
            switch_inst->cur_channel = switch_channel;
        }
    }
    /*
     * Write
     */
    status = adapter->write(adapter, base_addr, type, slaveAddr, buf, len, flags); // APP_SRTM_I2C_Write
    return status;
}

srtm_service_t SRTM_I2CService_Create(srtm_i2c_adapter_t adapter)
{
    srtm_i2c_service_t handle;

    SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_INFO, "%s\r\n", __func__);
    handle = (srtm_i2c_service_t)SRTM_Heap_Malloc(sizeof(struct _srtm_i2c_service));
    assert(handle);

    adapter->service = &handle->service;
    handle->adapter  = adapter;

    SRTM_List_Init(&handle->service.node);
    handle->service.dispatcher = NULL;
    handle->service.category   = SRTM_I2C_CATEGORY;
    handle->service.destroy    = SRTM_I2CService_Destroy;
    handle->service.request    = SRTM_I2CService_Request;
    handle->service.notify     = SRTM_I2CService_Notify;

    return &handle->service;
}

void SRTM_I2CService_Destroy(srtm_service_t service)
{
    srtm_i2c_service_t handle = (srtm_i2c_service_t)service;

    assert(service);

    SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_INFO, "%s\r\n", __func__);

    /* Service must be unregistered from dispatcher before destroy */
    assert(SRTM_List_IsEmpty(&service->node));

    SRTM_Heap_Free(handle);
}

void SRTM_I2CService_Reset(srtm_service_t service, srtm_peercore_t core)
{
    assert(service);

    SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_INFO, "%s\r\n", __func__);
}

static srtm_status_t SRTM_I2CService_Request(srtm_service_t service, srtm_request_t request)
{
    srtm_status_t status;
    srtm_channel_t channel;
    uint8_t command;
    uint32_t payloadLen;
    srtm_response_t response;
    struct _srtm_i2c_payload *i2cReq;
    struct _srtm_i2c_payload *i2cResp;

    assert(service->dispatcher);

    SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_INFO, "%s\r\n", __func__);

    channel    = SRTM_CommMessage_GetChannel(request);
    command    = SRTM_CommMessage_GetCommand(request);
    i2cReq     = (struct _srtm_i2c_payload *)SRTM_CommMessage_GetPayload(request);
    payloadLen = SRTM_CommMessage_GetPayloadLen(request);

    response =
        SRTM_Response_Create(channel, SRTM_I2C_CATEGORY, SRTM_I2C_VERSION, command, sizeof(struct _srtm_i2c_payload));
    if (!response)
    {
        return SRTM_Status_OutOfMemory;
    }

    i2cResp = (struct _srtm_i2c_payload *)SRTM_CommMessage_GetPayload(response);

    status = SRTM_Service_CheckVersion(service, request, SRTM_I2C_VERSION);
    if (status != SRTM_Status_Success || !i2cReq || payloadLen != sizeof(struct _srtm_i2c_payload))
    {
        SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_WARN, "%s: format error!\r\n", __func__);
        i2cResp->retCode = SRTM_I2C_RETURN_CODE_UNSUPPORTED;
    }
    else
    {
        SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_INFO,
                           "SRTM receive I2C request:cmd=%x, busID %d, slaveAddr 0x%x!, data %d bytes\r\n", command,
                           i2cReq->busID, i2cReq->slaveAddr, i2cReq->len);
        memcpy(i2cResp, i2cReq, sizeof(struct _srtm_i2c_payload));

        switch (command)
        {
            case SRTM_I2C_CMD_READ:
                status = SRTM_I2CService_ReadBus(service, i2cResp->busID, i2cResp->slaveAddr, i2cResp->data,
                                                 i2cReq->len, i2cReq->flags);
                i2cResp->retCode =
                    status == SRTM_Status_Success ? SRTM_I2C_RETURN_CODE_SUCEESS : SRTM_I2C_RETURN_CODE_FAIL;
                break;
            case SRTM_I2C_CMD_WRITE:
                status = SRTM_I2CService_WriteBus(service, i2cResp->busID, i2cResp->slaveAddr, i2cResp->data,
                                                  i2cReq->len, i2cReq->flags);
                i2cResp->retCode =
                    status == SRTM_Status_Success ? SRTM_I2C_RETURN_CODE_SUCEESS : SRTM_I2C_RETURN_CODE_FAIL;
                break;
            default:
                SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_WARN, "%s: command %d unsupported!\r\n", __func__, command);
                assert(false);
                break;
        }
    }

    return SRTM_Dispatcher_DeliverResponse(service->dispatcher, response);
}

static srtm_status_t SRTM_I2CService_Notify(srtm_service_t service, srtm_notification_t notif)
{
    SRTM_DEBUG_MESSAGE(SRTM_DEBUG_VERBOSE_WARN, "%s: command %d unsupported!\r\n", __func__,
                       SRTM_CommMessage_GetCommand(notif));

    return SRTM_Status_ServiceNotFound;
}

static void SRTM_I2C_HandleBusRead(srtm_dispatcher_t dispatcher, void *param1, void *param2)
{
    srtm_status_t status;
    srtm_i2c_payload_t i2c_payload = (srtm_i2c_payload_t)param1;
    srtm_service_t service         = (srtm_service_t)param2;
    status = SRTM_I2CService_ReadBus(service, i2c_payload->busID, i2c_payload->slaveAddr, i2c_payload->data,
                                     i2c_payload->len, i2c_payload->flags);
    i2c_payload->retCode = (status == SRTM_Status_Success) ? SRTM_I2C_RETURN_CODE_SUCEESS : SRTM_I2C_RETURN_CODE_FAIL;
}

static void SRTM_I2C_HandleBusWrite(srtm_dispatcher_t dispatcher, void *param1, void *param2)
{
    srtm_status_t status;
    srtm_i2c_payload_t i2c_payload = (srtm_i2c_payload_t)param1;
    srtm_service_t service         = (srtm_service_t)param2;
    status = SRTM_I2CService_WriteBus(service, i2c_payload->busID, i2c_payload->slaveAddr, i2c_payload->data,
                                      i2c_payload->len, i2c_payload->flags);
    i2c_payload->retCode = (status == SRTM_Status_Success) ? SRTM_I2C_RETURN_CODE_SUCEESS : SRTM_I2C_RETURN_CODE_FAIL;
}

srtm_status_t SRTM_I2C_RequestBusRead(
    srtm_service_t service, uint8_t busID, uint16_t slaveAddr, uint8_t *buf, uint8_t len)
{
    srtm_response_t response;
    srtm_status_t status;
    srtm_i2c_payload_t i2cReq;
    srtm_procedure_t proc;
    /*
     * Allocate an SRTM message and copy necessary information
     */
    assert(SRTM_I2C_BUF_LEN >= len);
    response          = SRTM_Response_Create(NULL, SRTM_I2C_CATEGORY, SRTM_I2C_VERSION, SRTM_I2C_CMD_READ,
                                    sizeof(struct _srtm_i2c_payload));
    i2cReq            = (struct _srtm_i2c_payload *)SRTM_CommMessage_GetPayload(response);
    i2cReq->busID     = busID;
    i2cReq->slaveAddr = slaveAddr;
    i2cReq->retCode   = SRTM_Status_Success;
    i2cReq->len       = len;
    memset(i2cReq->data, 0, len);
    /*
     * Call proc in sync manner
     */
    proc = SRTM_Procedure_Create(SRTM_I2C_HandleBusRead, i2cReq, service);
    SRTM_Dispatcher_CallProc(service->dispatcher, proc, SRTM_WAIT_FOR_EVER); /*synchronized call*/
    /*
     * Save proc exec result
     */
    memcpy(buf, i2cReq->data, len);
    status = (srtm_status_t)i2cReq->retCode;
    /*
     * Clean the allocated SRTM object
     */
    SRTM_Procedure_Destroy(proc);
    SRTM_Response_Destroy(response);

    return status;
}

srtm_status_t SRTM_I2C_RequestBusWrite(
    srtm_service_t service, uint8_t busID, uint16_t slaveAddr, uint8_t *buf, uint8_t len, uint8_t needStop)
{
    srtm_response_t response;
    srtm_status_t status;
    srtm_i2c_payload_t i2cReq;
    srtm_procedure_t proc;
    /*
     * Allocate an SRTM message and copy necessary information
     */
    assert(SRTM_I2C_BUF_LEN >= len);
    response          = SRTM_Response_Create(NULL, SRTM_I2C_CATEGORY, SRTM_I2C_VERSION, SRTM_I2C_CMD_WRITE,
                                    sizeof(struct _srtm_i2c_payload));
    i2cReq            = (struct _srtm_i2c_payload *)SRTM_CommMessage_GetPayload(response);
    i2cReq->busID     = busID;
    i2cReq->slaveAddr = slaveAddr;
    i2cReq->retCode   = SRTM_Status_Success;
    i2cReq->len       = len;
    i2cReq->flags     = needStop ? (SRTM_I2C_FLAG_NEED_STOP) : 0;

    memcpy(i2cReq->data, buf, len);
    /*
     * Call proc in sync manner
     */
    proc = SRTM_Procedure_Create(SRTM_I2C_HandleBusWrite, i2cReq, service);
    SRTM_Dispatcher_CallProc(service->dispatcher, proc, SRTM_WAIT_FOR_EVER); /*synchronized call*/
    /*
     * Save proc exec result
     */
    status = (srtm_status_t)i2cReq->retCode;
    /*
     * Clean the allocated SRTM object
     */
    SRTM_Procedure_Destroy(proc);
    SRTM_Response_Destroy(response);

    return status;
}
