//
// Copyright (c) 2010-2025 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
#include "renode_cfu.h"
static RenodeAgent* renodeAgent;

#define IO_THREADS 1

//=================================================
// RenodeAgent
//=================================================

RenodeAgent::RenodeAgent(Cfu *_cfu) {
    cfu = _cfu;
    cfu->tickCounter = 0;
}

void RenodeAgent::tick(bool countEnable, uint64_t steps)
{
    cfu->tick(countEnable, steps);
}

void RenodeAgent::reset()
{
    cfu->reset();
}

uint64_t RenodeAgent::execute(uint32_t functionID, uint32_t data0, uint32_t data1, int* error)
{
    uint64_t result = 0;

    try {
        result = cfu->execute(functionID, data0, data1, error);
        *error = CFU_OK;
    }
    catch(const char* msg) {
        if (msg == "Operation timeout") {
            *error = CFU_TIMEOUT;
        } else {
            *error = CFU_FAIL;
        }
    }
    return result;
}

void RenodeAgent::handleCustomRequestType(Protocol* message)
{
    log(LOG_LEVEL_WARNING, "Unhandled request type: %d", message->actionId);
}

void RenodeAgent::log(int level, const char* fmt, ...)
{
    char s[1024];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(s, 1024, fmt, ap);
    communicationChannel->log(level, s);
    va_end(ap);
}

//=================================================
// NativeCommunicationChannel
//=================================================

extern void handleSenderMessage(void* ptr);
EXTERNAL_AS(void, HandleSenderMessage, handleSenderMessage, voidptr);

void NativeCommunicationChannel::sendSender(const Protocol message)
{
    handleSenderMessage(new Protocol(message));
}

void NativeCommunicationChannel::log(int logLevel, const char* data)
{
    handleSenderMessage(new Protocol(logMessage, strlen(data) + 1, (uint64_t)data));
    handleSenderMessage(new Protocol(logMessage, 0, logLevel));
}

//=================================================
// Functions exported to Renode
//=================================================

uint64_t execute(uint32_t functionID, uint32_t data0, uint32_t data1, int* error)
{
    return renodeAgent->execute(functionID, data0, data1, error);
}

void initialize_native()
{
    renodeAgent = Init();
    renodeAgent->communicationChannel = new NativeCommunicationChannel();
}

void handle_request(Protocol* request)
{
    long ticks;
    switch(request->actionId) {
        case invalidAction:
            break;
        case tickClock:
            ticks = request->value - renodeAgent->cfu->tickCounter;
            if(ticks > 0) {
                renodeAgent->tick(false, ticks);
            }
            renodeAgent->cfu->tickCounter = 0;
            renodeAgent->communicationChannel->sendSender(Protocol(tickClock, 0, 0));
            break;
        case resetPeripheral:
            renodeAgent->reset();
            break;
        default:
            renodeAgent->handleCustomRequestType(request);
            break;
    }
}

void reset_peripheral()
{
    renodeAgent->reset();
}
