#include <cstring>

#include "xsched/utils/xassert.h"
#include "xsched/sched/protocol/operation.h"

using namespace xsched::sched;

std::shared_ptr<const Operation> Operation::CopyConstructor(const void *data)
{
    auto meta = (const OperationMeta *)data;
    switch (meta->type)
    {
    case kOperationTerminate:
        return std::make_shared<TerminateOperation>(data);
    case kOperationSched:
        return std::make_shared<SchedOperation>(data);
    case kOperationConfig:
        return std::make_shared<ConfigOperation>(data);
    default:
        XASSERT(false, "unknown operation type: %d", meta->type);
        return nullptr;
    }
}

SchedOperation::SchedOperation(const void *data)
{
    size_t size = ((const OperationData *)data)->size;
    OperationData *new_data = (OperationData *)malloc(size);
    memcpy(new_data, data, size);
    data_ = new_data;
}

SchedOperation::SchedOperation(const ProcessStatus &status)
{
    PID pid = status.info.pid;
    size_t running_cnt = status.running_xqueues.size();
    size_t suspended_cnt = status.suspended_xqueues.size();
    size_t xqueue_cnt = running_cnt + suspended_cnt;
    
    size_t size = offsetof(OperationData, handles) + xqueue_cnt * sizeof(XQueueHandle);
    OperationData *data = (OperationData *)malloc(size);
    data_ = data;

    data->meta.type = kOperationSched;
    data->meta.pid = pid;
    data->size = size;
    data->running_cnt = running_cnt;
    data->suspended_cnt = suspended_cnt;

    xqueue_cnt = 0;
    for (auto &handle : status.running_xqueues) data->handles[xqueue_cnt++] = handle;
    for (auto &handle : status.suspended_xqueues) data->handles[xqueue_cnt++] = handle;
}

SchedOperation::~SchedOperation()
{
    if (data_) free((void *)data_);
}

ConfigOperation::ConfigOperation(const void *data)
{
    size_t size = ConfigOperation::Size();
    OperationData *new_data = (OperationData *)malloc(size);
    memcpy(new_data, data, size);
    data_ = new_data;
}

ConfigOperation::ConfigOperation(PID pid, XQueueHandle handle, XPreemptLevel level,
                                 int64_t threshold, int64_t batch_size)
{
    OperationData *data = (OperationData *)malloc(ConfigOperation::Size());

    data->meta.type = kOperationConfig;
    data->meta.pid = pid;
    data->handle = handle;
    data->level = level;
    data->threshold = threshold;
    data->batch_size = batch_size;

    data_ = data;
}

ConfigOperation::~ConfigOperation()
{
    if (data_) free((void *)data_);
}