#include <msgpack.hpp>

#define BOOST_TEST_MODULE visitor
#include <boost/test/unit_test.hpp>

#include <sstream>

BOOST_AUTO_TEST_CASE(dummy)
{
}

#if MSGPACK_DEFAULT_API_VERSION >= 2

struct json_like_visitor : msgpack::null_visitor {
    json_like_visitor(std::string& s):m_s(s) {}

    bool visit_nil() {
        m_s += "null";
        return true;
    }
    bool visit_boolean(bool v) {
        if (v) m_s += "true";
        else m_s += "false";
        return true;
    }
    bool visit_positive_integer(uint64_t v) {
        std::stringstream ss;
        ss << v;
        m_s += ss.str();
        return true;
    }
    bool visit_negative_integer(int64_t v) {
        std::stringstream ss;
        ss << v;
        m_s += ss.str();
        return true;
    }
    bool visit_str(const char* v, uint32_t size) {
        // I omit escape process.
        m_s += '"' + std::string(v, size) + '"';
        return true;
    }
    bool start_array(uint32_t /*num_elements*/) {
        m_s += "[";
        return true;
    }
    bool end_array_item() {
        m_s += ",";
        return true;
    }
    bool end_array() {
        m_s.erase(m_s.size() - 1, 1); // remove the last ','
        m_s += "]";
        return true;
    }
    bool start_map(uint32_t /*num_kv_pairs*/) {
        m_s += "{";
        return true;
    }
    bool end_map_key() {
        m_s += ":";
        return true;
    }
    bool end_map_value() {
        m_s += ",";
        return true;
    }
    bool end_map() {
        m_s.erase(m_s.size() - 1, 1); // remove the last ','
        m_s += "}";
        return true;
    }
    void parse_error(size_t /*parsed_offset*/, size_t /*error_offset*/) {
        BOOST_CHECK(false);
    }
    void insufficient_bytes(size_t /*parsed_offset*/, size_t /*error_offset*/) {
        BOOST_CHECK(false);
    }
    std::string& m_s;
};

BOOST_AUTO_TEST_CASE(json_like)
{
    std::stringstream ss;
    msgpack::packer<std::stringstream> p(ss);
    p.pack_map(1);
    p.pack("key");
    p.pack_array(3);
    p.pack(42);
    p.pack_nil();
    p.pack(true);

    std::string json_like;
    json_like_visitor v(json_like);
    std::size_t off = 0;
    std::string const& str = ss.str();
    bool ret = msgpack::parse(str.data(), str.size(), off, v);
    BOOST_CHECK(ret);
    BOOST_CHECK_EQUAL("{\"key\":[42,null,true]}", json_like);
}

struct parse_error_check_visitor : msgpack::null_visitor {
    parse_error_check_visitor(bool& called):m_called(called) {}
    void parse_error(size_t parsed_offset, size_t error_offset) {
        BOOST_CHECK_EQUAL(static_cast<size_t>(1), parsed_offset);
        BOOST_CHECK_EQUAL(static_cast<size_t>(2), error_offset);
        m_called = true;
    }
    bool& m_called;
};

BOOST_AUTO_TEST_CASE(parse_error)
{
    bool called = false;
    parse_error_check_visitor v(called);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x93u), 0x01u, static_cast<char>(0xc1u), 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK(called);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(2u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct insuf_bytes_check_visitor : msgpack::null_visitor {
    insuf_bytes_check_visitor(bool& called):m_called(called) {}
    void insufficient_bytes(size_t parsed_offset, size_t error_offset) {
        BOOST_CHECK_EQUAL(static_cast<size_t>(2), parsed_offset);
        BOOST_CHECK_EQUAL(static_cast<size_t>(3), error_offset);
        m_called = true;
    }
    bool& m_called;
};

BOOST_AUTO_TEST_CASE(insuf_bytes)
{
    bool called = false;
    insuf_bytes_check_visitor v(called);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x93u), 0x01u, 0x01u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK(called);
    // Even if MSGPACK_DEFAULT_API_VERSION is 2, then off is updated
    // in the case of insufficient bytes.
    BOOST_CHECK_EQUAL(3u, off);
}

struct return_false_array_val_visitor : msgpack::null_visitor {
    return_false_array_val_visitor(std::size_t& times):m_times(times) {}
    bool visit_positive_integer(uint64_t) {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_array_val)
{
    std::size_t times = 0;
    return_false_array_val_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x93u), 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(2u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_start_array_item_visitor : msgpack::null_visitor {
    return_false_start_array_item_visitor(std::size_t& times):m_times(times) {}
    bool start_array_item() {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_start_array_item)
{
    std::size_t times = 0;
    return_false_start_array_item_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x93u), 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(2u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_end_array_item_visitor : msgpack::null_visitor {
    return_false_end_array_item_visitor(std::size_t& times):m_times(times) {}
    bool end_array_item() {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_end_array_item)
{
    std::size_t times = 0;
    return_false_end_array_item_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x93u), 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(2u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_start_array_visitor : msgpack::null_visitor {
    bool start_array(uint32_t) {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_start_array)
{
    return_false_start_array_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x93u), 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(0u, off);
}

struct return_false_start_array0_visitor : msgpack::null_visitor {
    bool start_array(uint32_t) {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_start_array0)
{
    return_false_start_array0_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x90u) };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(0u, off);
}

struct return_false_end_array_visitor : msgpack::null_visitor {
    bool end_array() {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_end_array)
{
    return_false_end_array_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x93u), 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(3u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_end_array0_visitor : msgpack::null_visitor {
    bool end_array() {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_end_array0)
{
    return_false_end_array0_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x90u) };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(0u, off);
}

struct return_false_map_val_visitor : msgpack::null_visitor {
    return_false_map_val_visitor(std::size_t& times):m_times(times) {}
    bool visit_positive_integer(uint64_t) {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_map_val)
{
    std::size_t times = 0;
    return_false_map_val_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x83u), 0x01u, 0x02u, 0x03u, 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(2u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_start_map_key_visitor : msgpack::null_visitor {
    return_false_start_map_key_visitor(std::size_t& times):m_times(times) {}
    bool start_map_key() {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_start_map_key)
{
    std::size_t times = 0;
    return_false_start_map_key_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x83u), 0x01u, 0x02u, 0x03u, 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(3u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_end_map_key_visitor : msgpack::null_visitor {
    return_false_end_map_key_visitor(std::size_t& times):m_times(times) {}
    bool end_map_key() {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_end_map_key)
{
    std::size_t times = 0;
    return_false_end_map_key_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x83u), 0x01u, 0x02u, 0x03u, 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(3u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_start_map_value_visitor : msgpack::null_visitor {
    return_false_start_map_value_visitor(std::size_t& times):m_times(times) {}
    bool start_map_value() {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_start_map_value)
{
    std::size_t times = 0;
    return_false_start_map_value_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x83u), 0x01u, 0x02u, 0x03u, 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(4u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_end_map_value_visitor : msgpack::null_visitor {
    return_false_end_map_value_visitor(std::size_t& times):m_times(times) {}
    bool end_map_value() {
        if (++m_times == 2) return false;
        return true;
    }
    std::size_t& m_times;
};

BOOST_AUTO_TEST_CASE(return_false_end_map_value)
{
    std::size_t times = 0;
    return_false_end_map_value_visitor v(times);
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x83u), 0x01u, 0x02u, 0x03u, 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(2u, times);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(4u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_start_map_visitor : msgpack::null_visitor {
    bool start_map(uint32_t) {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_start_map)
{
    return_false_start_map_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x83u), 0x01u, 0x02u, 0x03u, 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(0u, off);
}

struct return_false_start_map0_visitor : msgpack::null_visitor {
    bool start_map(uint32_t) {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_start_map0)
{
    return_false_start_map0_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x80u) };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(0u, off);
}

struct return_false_end_map_visitor : msgpack::null_visitor {
    bool end_map() {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_end_map)
{
    return_false_end_map_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x83u), 0x01u, 0x02u, 0x03u, 0x01u, 0x02u, 0x03u };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
#if MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(0u, off);
#else  // MSGPACK_DEFAULT_API_VERSION < 3
    BOOST_CHECK_EQUAL(6u, off);
#endif // MSGPACK_DEFAULT_API_VERSION < 3
}

struct return_false_end_map0_visitor : msgpack::null_visitor {
    bool end_map() {
        return false;
    }
};

BOOST_AUTO_TEST_CASE(return_false_end_map0)
{
    return_false_end_map0_visitor v;
    std::size_t off = 0;
    char const data[] = { static_cast<char>(0x80u) };
    bool ret = msgpack::parse(data, sizeof(data), off, v);
    BOOST_CHECK(!ret);
    BOOST_CHECK_EQUAL(0u, off);
}

#endif // MSGPACK_DEFAULT_API_VERSION >= 1
