
#include "wrap-sstream.hpp"

#include "sched.hpp"
#include "streambuffer.hpp"
#include "luastack.hpp"

#include <ostream>

bool SchedEntry::operator < (const SchedEntry &other) const {
    if (clock_ < other.clock_) return true;
    if (clock_ > other.clock_) return false;
    if (thread_id_ < other.thread_id_) return true;
    if (thread_id_ > other.thread_id_) return false;
    if (place_id_ < other.place_id_) return true;
    if (place_id_ > other.place_id_) return false;
    return false;
}

eng::string SchedEntry::debug_string() const {
    eng::ostringstream oss;
    oss << "(" << clock_ << "," << thread_id_ << "," << place_id_ << ")";
    return oss.str();
}

void Schedule::add(int64_t clk, int64_t thid, int64_t plid) {
    assert(plid != 0);
    assert(thid != 0);
    schedule_.insert(SchedEntry(clk, thid, plid));
}

bool Schedule::ready(int64_t clk) const {
    return (!schedule_.empty()) && (schedule_.begin()->clock() <= clk);
}

SchedEntry Schedule::pop() {
    assert(!schedule_.empty());
    SchedEntry result = *schedule_.begin();
    schedule_.erase(schedule_.begin());
    return result;
}

eng::string Schedule::debug_string() {
    eng::ostringstream oss;
    for (const SchedEntry &se : schedule_) {
        oss << se.debug_string();
    }
    return oss.str();
}

void Schedule::serialize(StreamBuffer *sb) {
    sb->write_uint32(schedule_.size());
    for (const SchedEntry &entry : schedule_) {
        sb->write_int64(entry.clock_);
        sb->write_int64(entry.thread_id_);
        sb->write_int64(entry.place_id_);
    }
}

void Schedule::deserialize(StreamBuffer *sb) {
    schedule_.clear();
    size_t nentry = sb->read_uint32();
    for (size_t i = 0; i < nentry; i++) {
        int64_t clock = sb->read_int64();
        int64_t thread_id = sb->read_int64();
        int64_t place_id = sb->read_int64();
        add(clock, thread_id, place_id);
    }
}

LuaDefine(unittests_scheduler, "", "some unit tests") {
    Schedule s, xs;
    StreamBuffer sb;

    // Put some stuff into a scheduler.
    s.add(1, 5, 3);
    s.add(4, 3, 2);
    s.add(1, 3, 2);
    s.add(4, 2, 3);

    // Test serialize and deserialize.
    LuaAssert(L, s.debug_string() == "(1,3,2)(1,5,3)(4,2,3)(4,3,2)");
    s.serialize(&sb);
    xs.deserialize(&sb);
    LuaAssert(L, xs.debug_string() == "(1,3,2)(1,5,3)(4,2,3)(4,3,2)");

    // Verify that pop, ready, and empty do the right thing.
    LuaAssert(L, s.ready(0) == false)
    LuaAssert(L, s.ready(1) == true)
    LuaAssert(L, s.pop().debug_string() == "(1,3,2)");
    LuaAssert(L, s.ready(0) == false)
    LuaAssert(L, s.ready(1) == true)
    LuaAssert(L, s.pop().debug_string() == "(1,5,3)");
    LuaAssert(L, s.debug_string() == "(4,2,3)(4,3,2)");
    LuaAssert(L, s.pop().debug_string() == "(4,2,3)");
    LuaAssert(L, s.pop().debug_string() == "(4,3,2)");
    LuaAssert(L, s.ready(10000) == false);
    LuaAssert(L, s.empty());
    LuaAssert(L, s.debug_string() == "");

    // Test serialization of an empty streambuffer.
    s.serialize(&sb);
    xs.deserialize(&sb);
    LuaAssert(L, xs.debug_string() == "");

    return 0;
}
