
#include "wrap-string.hpp"
#include "wrap-map.hpp"
#include "wrap-deque.hpp"
#include "wrap-sstream.hpp"

#include "invocation.hpp"

const eng::string &InvocationData::get(const eng::string &key) const {
    static eng::string blank_;
    auto iter = find(key);
    if (iter == end()) {
        return blank_;
    } else {
        return iter->second;
    }
}

void InvocationData::serialize(StreamBuffer *sb) const {
    assert(int(size()) < 65536);
    sb->write_uint16(size());
    for (const auto &pair : *this) {
        sb->write_string(pair.first);
        sb->write_string(pair.second);
    }
}

void InvocationData::deserialize(StreamBuffer *sb) {
    clear();
    int size = sb->read_uint16();
    for (int i = 0; i < size; i++) {
        eng::string key = sb->read_string();
        eng::string val = sb->read_string();
        (*this)[key] = val;
    }
}

Invocation::Invocation() : kind_(KIND_INVALID), actor_(0), place_(0) {}

Invocation::Invocation(Kind kind, int64_t actor, int64_t place, const eng::string &action, const InvocationData &data) 
    : kind_(kind), actor_(actor), place_(place), action_(action), data_(data) {}

Invocation::Invocation(Kind kind, int64_t actor, int64_t place, const eng::string &action) 
    : kind_(kind), actor_(actor), place_(place), action_(action) {}

void Invocation::serialize(StreamBuffer *sb) const {
    sb->write_uint8(kind_);
    sb->write_int64(actor_);
    sb->write_int64(place_);
    sb->write_string(action_);
    data_.serialize(sb);
}

void Invocation::deserialize(StreamBuffer *sb) {
    kind_ = Kind(sb->read_uint8());
    actor_ = sb->read_int64();
    place_ = sb->read_int64();
    action_ = sb->read_string();
    data_.deserialize(sb);
}

eng::string Invocation::debug_string() {
    eng::ostringstream oss;
    oss << "inv[";
    switch (kind_) {
    case KIND_INVALID: oss << "invalid"; break;
    case KIND_PLAN: oss << "plan"; break;
    case KIND_LUA: oss << "lua"; break;
    case KIND_FLUSH_PRINTS: oss << "flush_prints"; break;
    case KIND_TICK: oss << "tick"; break;
    case KIND_LUA_SOURCE: oss << "lua_source"; break;
    default: oss << "UNKNOWN"; break;
    }
    oss << " a=" << actor_;
    oss << " p=" << place_;
    if (kind_ != KIND_LUA_SOURCE) {
        oss << " " << action_;
    }
    for (const auto &pair : data_) {
        oss << " " << pair.first << "=" << pair.second;
    }
    oss << "]";
    return oss.str();
}
