/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <stdarg.h>
#include <Metview.h>

MvProtocol::MvProtocol(const char* name) :
    Id(nullptr)
{
    add_service_callback(MvApplication::getService(), name, _serve, (void*)this);
}

MvProtocol::~MvProtocol() = default;

void MvProtocol::addCallbackKeyword(const char* name)
{
    add_service_callback(MvApplication::getService(), name, _serve, (void*)this);
}

void MvProtocol::_serve(svcid* id, request* r, void* obj)
{
    auto* s = (MvProtocol*)obj;
    s->_call_serve(id, r);
}

void MvProtocol::_call_serve(svcid* id, request* r)
{
    Id = id;

    MvRequest in(r);

    Error = 0;
    callback(in);
}

const char* MvProtocol::iconName()
{
    return get_value(Id->r->next, "_NAME", 0);
}

const char* MvProtocol::iconClass()
{
    return get_value(Id->r->next, "_CLASS", 0);
}

void MvProtocol::setError(err e)
{
    Error = e;
    set_svc_err(Id, e);
}

int MvProtocol::getError()
{
    return get_svc_err(Id);
}

const char* MvProtocol::getMessage(int n)
{
    return get_svc_msg(Id, n);
}

void MvProtocol::setError(err e, const char* fmt, ...)
{
    char buffer[1024];

    Error = e;

    va_list list;
    va_start(list, fmt);
    vsprintf(buffer, fmt, list);
    va_end(list);

    set_svc_err(Id, e);
    set_svc_msg(Id, "%s", buffer);
}

void MvProtocol::_clear_id()
{
    Id = nullptr;
}

void MvProtocol::sendReply(const MvRequest& answer)
{
    send_reply(Id, answer);
    _clear_id();
}

void MvProtocol::reDispatch(MvRequest& r)
{
    re_dispatch(Id, r);
    _clear_id();
}

void MvProtocol::sendProgress(const MvRequest& r)
{
    send_progress(Id, nullptr, r);
}

void MvProtocol::sendProgress(const std::string& msg)
{
    // since there are various places in the code that assume
    // buffer sizes of 1024, we will trim any string longer than
    // that
    std::string trimmed;
    if (msg.size() > 1023) {
        trimmed = msg.substr(0, 1014) + std::string("-TRIMMED");
    }
    else {
        trimmed = msg;
    }
    send_progress(Id, trimmed.c_str(), nullptr);
}

void MvProtocol::sendProgress(const char* fmt, ...)
{
    char buffer[1024];
    va_list list;
    va_start(list, fmt);
    vsprintf(buffer, fmt, list);
    va_end(list);
    send_progress(Id, buffer, nullptr);
}

const char* MvProtocol::getSource()
{
    return get_value(Id->r, "SOURCE", 0);
}

const char* MvProtocol::getTarget()
{
    return get_value(Id->r, "TARGET", 0);
}

const char* MvProtocol::getAction()
{
    return get_value(Id->r->next, "_ACTION", 0);
}

MvRequest MvProtocol::getMode()
{
    return MvRequest(get_subrequest(Id->r->next, "_MODE", 0), false);
}

void* MvProtocol::getReference()
{
    return (void*)get_svc_ref(Id);
}


MvReply::MvReply(const char* name)
{
    add_reply_callback(MvApplication::getService(), name,
                       _serve, (void*)this);
}

void MvReply::addCallbackKeyword(const char* name)
{
    add_reply_callback(MvApplication::getService(), name, _serve, (void*)this);
}

MvProgress::MvProgress(const char* name)
{
    add_progress_callback(MvApplication::getService(), name,
                          _serve, (void*)this);
}

void MvProgress::addCallbackKeyword(const char* name)
{
    add_progress_callback(MvApplication::getService(), name, _serve, (void*)this);
}

MvMessage::MvMessage(const char* name)
{
    add_message_callback(MvApplication::getService(), name,
                         _serve, (void*)this);
}

MvTransaction::MvTransaction(MvTransaction* from)
{
    Id = from->Id;
    Error = from->Error;
}

void MvTransaction::_call_serve(svcid* id, request* r)
{
    Id = id;
    Error = 0;

    MvTransaction* t = cloneSelf();
    MvRequest in(r);

    t->callback(in);
}

void MvTransaction::_clear_id()
{
    Id = nullptr;
    delete this;
}
