#include "msgpack.hpp"

#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <deque>
#include <set>
#include <list>
#include <limits>

#include <gtest/gtest.h>

#if defined(_MSC_VER)
#define msgpack_rand() ((double)rand() / RAND_MAX)
#else  // _MSC_VER
#define msgpack_rand() drand48()
#endif // _MSC_VER

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

using namespace std;

const unsigned int kLoop = 10000;
const unsigned int kElements = 100;
const double kEPS = 1e-10;

#define GEN_TEST(test_type)                                           \
  do {                                                                \
    vector<test_type> v;                                              \
    v.push_back(0);                                                   \
    v.push_back(1);                                                   \
    v.push_back(2);                                                   \
    v.push_back(numeric_limits<test_type>::min());                    \
    v.push_back(numeric_limits<test_type>::max());                    \
    for (unsigned int i = 0; i < kLoop; i++)                          \
      v.push_back(rand());                                            \
    for (unsigned int i = 0; i < v.size() ; i++) {                    \
      msgpack::sbuffer sbuf;                                          \
      test_type val1 = v[i];                                          \
      msgpack::pack(sbuf, val1);                                      \
      msgpack::unpacked ret;                                          \
      msgpack::unpack(ret, sbuf.data(), sbuf.size());                 \
      EXPECT_EQ(val1, ret.get().as<test_type>());                     \
    }                                                                 \
} while(0)

TEST(MSGPACK, simple_buffer_char)
{
  GEN_TEST(char);
}

TEST(MSGPACK, simple_buffer_signed_char)
{
  GEN_TEST(signed char);
}

TEST(MSGPACK, simple_buffer_unsigned_char)
{
  GEN_TEST(unsigned char);
}


TEST(MSGPACK, simple_buffer_short)
{
  GEN_TEST(short);
}

TEST(MSGPACK, simple_buffer_int)
{
  GEN_TEST(int);
}

TEST(MSGPACK, simple_buffer_long)
{
  GEN_TEST(long);
}

TEST(MSGPACK, simple_buffer_long_long)
{
  GEN_TEST(long long);
}

TEST(MSGPACK, simple_buffer_unsigned_short)
{
  GEN_TEST(unsigned short);
}

TEST(MSGPACK, simple_buffer_unsigned_int)
{
  GEN_TEST(unsigned int);
}

TEST(MSGPACK, simple_buffer_unsigned_long)
{
  GEN_TEST(unsigned long);
}

TEST(MSGPACK, simple_buffer_unsigned_long_long)
{
  GEN_TEST(unsigned long long);
}

TEST(MSGPACK, simple_buffer_uint8)
{
  GEN_TEST(uint8_t);
}

TEST(MSGPACK, simple_buffer_uint16)
{
  GEN_TEST(uint16_t);
}

TEST(MSGPACK, simple_buffer_uint32)
{
  GEN_TEST(uint32_t);
}

TEST(MSGPACK, simple_buffer_uint64)
{
  GEN_TEST(uint64_t);
}

TEST(MSGPACK, simple_buffer_int8)
{
  GEN_TEST(int8_t);
}

TEST(MSGPACK, simple_buffer_int16)
{
  GEN_TEST(int16_t);
}

TEST(MSGPACK, simple_buffer_int32)
{
  GEN_TEST(int32_t);
}

TEST(MSGPACK, simple_buffer_int64)
{
  GEN_TEST(int64_t);
}

TEST(MSGPACK, simple_buffer_float)
{
  vector<float> v;
  v.push_back(0.0);
  v.push_back(-0.0);
  v.push_back(1.0);
  v.push_back(-1.0);
  v.push_back(numeric_limits<float>::min());
  v.push_back(numeric_limits<float>::max());
  v.push_back(nanf("tag"));
  if (numeric_limits<float>::has_infinity) {
    v.push_back(numeric_limits<float>::infinity());
    v.push_back(-numeric_limits<float>::infinity());
  }
  if (numeric_limits<float>::has_quiet_NaN) {
    v.push_back(numeric_limits<float>::quiet_NaN());
  }
  if (numeric_limits<float>::has_signaling_NaN) {
    v.push_back(numeric_limits<float>::signaling_NaN());
  }

  for (unsigned int i = 0; i < kLoop; i++) {
    v.push_back(static_cast<float>(msgpack_rand()));
    v.push_back(static_cast<float>(-msgpack_rand()));
  }
  for (unsigned int i = 0; i < v.size() ; i++) {
    msgpack::sbuffer sbuf;
    float val1 = v[i];
    msgpack::pack(sbuf, val1);
    msgpack::unpacked ret;
    msgpack::unpack(ret, sbuf.data(), sbuf.size());
    float val2 = ret.get().as<float>();

    if (std::isnan(val1))
      EXPECT_TRUE(std::isnan(val2));
    else if (std::isinf(val1))
      EXPECT_TRUE(std::isinf(val2));
    else
      EXPECT_TRUE(fabs(val2 - val1) <= kEPS);
  }
}

namespace {
template<typename F, typename I>
struct TypePair {
  typedef F float_type;
  typedef I integer_type;
};
} // namespace

template <typename T>
class IntegerToFloatingPointTest : public testing::Test {
};
TYPED_TEST_CASE_P(IntegerToFloatingPointTest);

TYPED_TEST_P(IntegerToFloatingPointTest, simple_buffer)
{
  typedef typename TypeParam::float_type float_type;
  typedef typename TypeParam::integer_type integer_type;
  vector<integer_type> v;
  v.push_back(0);
  v.push_back(1);
  if (numeric_limits<integer_type>::is_signed) v.push_back(-1);
  else v.push_back(2);
  for (unsigned int i = 0; i < kLoop; i++) {
    v.push_back(rand() % 0x7FFFFF);
  }
  for (unsigned int i = 0; i < v.size() ; i++) {
    msgpack::sbuffer sbuf;
    integer_type val1 = v[i];
    msgpack::pack(sbuf, val1);
    msgpack::unpacked ret;
    msgpack::unpack(ret, sbuf.data(), sbuf.size());
    float_type val2 = ret.get().as<float_type>();
    EXPECT_TRUE(fabs(val2 - val1) <= kEPS);
  }
}

REGISTER_TYPED_TEST_CASE_P(IntegerToFloatingPointTest,
                           simple_buffer);

typedef testing::Types<TypePair<float, signed long long>,
                       TypePair<float, unsigned long long>,
                       TypePair<double, signed long long>,
                       TypePair<double, unsigned long long> > IntegerToFloatingPointTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(IntegerToFloatingPointTestInstance,
                              IntegerToFloatingPointTest,
                              IntegerToFloatingPointTestTypes);

TEST(MSGPACK, simple_buffer_double)
{
  vector<double> v;
  v.push_back(0.0);
  v.push_back(-0.0);
  v.push_back(1.0);
  v.push_back(-1.0);
  v.push_back(numeric_limits<double>::min());
  v.push_back(numeric_limits<double>::max());
  v.push_back(nanf("tag"));
  if (numeric_limits<double>::has_infinity) {
    v.push_back(numeric_limits<double>::infinity());
    v.push_back(-numeric_limits<double>::infinity());
  }
  if (numeric_limits<double>::has_quiet_NaN) {
    v.push_back(numeric_limits<double>::quiet_NaN());
  }
  if (numeric_limits<double>::has_signaling_NaN) {
    v.push_back(numeric_limits<double>::signaling_NaN());
  }
  for (unsigned int i = 0; i < kLoop; i++) {
    v.push_back(msgpack_rand());
    v.push_back(-msgpack_rand());
  }

  for (unsigned int i = 0; i < kLoop; i++) {
    v.push_back(msgpack_rand());
    v.push_back(-msgpack_rand());
  }
  for (unsigned int i = 0; i < v.size() ; i++) {
    msgpack::sbuffer sbuf;
    double val1 = v[i];
    msgpack::pack(sbuf, val1);
    msgpack::unpacked ret;
    msgpack::unpack(ret, sbuf.data(), sbuf.size());
    double val2 = ret.get().as<double>();

    if (std::isnan(val1))
      EXPECT_TRUE(std::isnan(val2));
    else if (std::isinf(val1))
      EXPECT_TRUE(std::isinf(val2));
    else
      EXPECT_TRUE(fabs(val2 - val1) <= kEPS);
  }
}

TEST(MSGPACK, simple_buffer_nil)
{
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  packer.pack_nil();
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(ret.get().type, msgpack::type::NIL);
}

TEST(MSGPACK, simple_buffer_true)
{
  msgpack::sbuffer sbuf;
  bool val1 = true;
  msgpack::pack(sbuf, val1);
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  bool val2 = ret.get().as<bool>();
  EXPECT_EQ(val1, val2);
}

TEST(MSGPACK, simple_buffer_false)
{
  msgpack::sbuffer sbuf;
  bool val1 = false;
  msgpack::pack(sbuf, val1);
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  bool val2 = ret.get().as<bool>();
  EXPECT_EQ(val1, val2);
}

TEST(MSGPACK, simple_buffer_fixext1)
{
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char const buf [] = { 2 };

  packer.pack_ext(sizeof(buf), 1);
  packer.pack_ext_body(buf, sizeof(buf));
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(1ul, ret.get().via.ext.size);
  EXPECT_EQ(1, ret.get().via.ext.type());
  EXPECT_EQ(2, ret.get().via.ext.data()[0]);
}

TEST(MSGPACK, simple_buffer_fixext2)
{
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char const buf [] = { 2, 3 };

  packer.pack_ext(sizeof(buf), 0);
  packer.pack_ext_body(buf, sizeof(buf));
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(2ul, ret.get().via.ext.size);
  EXPECT_EQ(0, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_fixext4)
{
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char const buf [] = { 2, 3, 4, 5 };

  packer.pack_ext(sizeof(buf), 1);
  packer.pack_ext_body(buf, sizeof(buf));
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(4ul, ret.get().via.ext.size);
  EXPECT_EQ(1, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_fixext8)
{
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char const buf [] = { 2, 3, 4, 5, 6, 7, 8, 9 };

  packer.pack_ext(sizeof(buf), 1);
  packer.pack_ext_body(buf, sizeof(buf));
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(8ul, ret.get().via.ext.size);
  EXPECT_EQ(1, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_fixext16)
{
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char const buf [] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };

  packer.pack_ext(sizeof(buf), 1);
  packer.pack_ext_body(buf, sizeof(buf));
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(16ul, ret.get().via.ext.size);
  EXPECT_EQ(1, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_fixext_1byte_0)
{
  std::size_t const size = 0;
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);

  packer.pack_ext(size, 77);
  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(size, ret.get().via.ext.size);
  EXPECT_EQ(77, ret.get().via.ext.type());
}

TEST(MSGPACK, simple_buffer_fixext_1byte_255)
{
  std::size_t const size = 255;
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char buf[size];
  for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
  packer.pack_ext(sizeof(buf), 77);
  packer.pack_ext_body(buf, sizeof(buf));

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(size, ret.get().via.ext.size);
  EXPECT_EQ(77, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_fixext_2byte_256)
{
  std::size_t const size = 256;
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char buf[size];
  for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
  packer.pack_ext(sizeof(buf), 77);
  packer.pack_ext_body(buf, sizeof(buf));

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(size, ret.get().via.ext.size);
  EXPECT_EQ(77, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_fixext_2byte_65535)
{
  std::size_t const size = 65535;
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char buf[size];
  for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
  packer.pack_ext(sizeof(buf), 77);
  packer.pack_ext_body(buf, sizeof(buf));

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(size, ret.get().via.ext.size);
  EXPECT_EQ(77, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_fixext_4byte_65536)
{
  std::size_t const size = 65536;
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char buf[size];
  for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
  packer.pack_ext(sizeof(buf), 77);
  packer.pack_ext_body(buf, sizeof(buf));

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  EXPECT_EQ(size, ret.get().via.ext.size);
  EXPECT_EQ(77, ret.get().via.ext.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data()));
}

TEST(MSGPACK, simple_buffer_ext_convert)
{
  std::size_t const size = 65536;
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char buf[size];
  for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
  packer.pack_ext(sizeof(buf), 77);
  packer.pack_ext_body(buf, sizeof(buf));

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  msgpack::type::ext e;
  ret.get().convert(e);
  EXPECT_EQ(size, e.size());
  EXPECT_EQ(77, e.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), e.data()));
}

TEST(MSGPACK, simple_buffer_ext_pack_convert)
{
  std::size_t const size = 65536;
  msgpack::sbuffer sbuf;
  msgpack::type::ext val1(77, size);
  char* buf = val1.data();
  for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
  msgpack::pack(sbuf, val1);

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  msgpack::type::ext val2;
  ret.get().convert(val2);
  EXPECT_EQ(size, val2.size());
  EXPECT_EQ(77, val2.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), val2.data()));
}

TEST(MSGPACK, simple_buffer_ext_ref_convert)
{
  std::size_t const size = 65536;
  msgpack::sbuffer sbuf;
  msgpack::packer<msgpack::sbuffer> packer(sbuf);
  char buf[size];
  for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
  packer.pack_ext(sizeof(buf), 77);
  packer.pack_ext_body(buf, sizeof(buf));

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  msgpack::type::ext_ref er;
  ret.get().convert(er);
  EXPECT_EQ(size, er.size());
  EXPECT_EQ(77, er.type());
  EXPECT_TRUE(
      std::equal(buf, buf + sizeof(buf), er.data()));
}

TEST(MSGPACK, simple_buffer_ext_ref_pack_convert)
{
  std::size_t const buf_size = 65536;
  std::size_t const data_size = buf_size - 1;
  msgpack::sbuffer sbuf;
  char buf[buf_size];
  buf[0] = static_cast<char>(77);
  for (std::size_t i = 0; i != data_size; ++i) buf[i + 1] = static_cast<char>(i);
  msgpack::pack(sbuf, msgpack::type::ext_ref(buf, buf_size));

  msgpack::unpacked ret;
  msgpack::unpack(ret, sbuf.data(), sbuf.size());
  msgpack::type::ext_ref val2;
  ret.get().convert(val2);
  EXPECT_EQ(data_size, val2.size());
  EXPECT_EQ(77, val2.type());
  EXPECT_TRUE(
      std::equal(&buf[1], &buf[buf_size], val2.data()));
}

TEST(MSGPACK_STL, simple_buffer_string)
{
  for (unsigned int k = 0; k < kLoop; k++) {
    string val1;
    for (unsigned int i = 0; i < kElements; i++)
      val1 += 'a' + rand() % 26;
    msgpack::sbuffer sbuf;
    msgpack::pack(sbuf, val1);
    msgpack::unpacked ret;
    msgpack::unpack(ret, sbuf.data(), sbuf.size());
    EXPECT_EQ(ret.get().type, msgpack::type::STR);
    string val2 = ret.get().as<string>();
    EXPECT_EQ(val1.size(), val2.size());
    EXPECT_EQ(val1, val2);
  }
}

TEST(MSGPACK_STL, simple_buffer_cstring)
{
  for (unsigned int k = 0; k < kLoop; k++) {
    string val1;
    for (unsigned int i = 0; i < kElements; i++)
      val1 += 'a' + rand() % 26;
    msgpack::sbuffer sbuf;
    msgpack::pack(sbuf, val1.c_str());
    msgpack::unpacked ret;
    msgpack::unpack(ret, sbuf.data(), sbuf.size());
    EXPECT_EQ(ret.get().type, msgpack::type::STR);
    string val2 = ret.get().as<string>();
    EXPECT_EQ(val1.size(), val2.size());
    EXPECT_EQ(val1, val2);
  }
}

TEST(MSGPACK_STL, simple_buffer_non_const_cstring)
{
  for (unsigned int k = 0; k < kLoop; k++) {
    string val1;
    for (unsigned int i = 0; i < kElements; i++)
      val1 += 'a' + rand() % 26;
    msgpack::sbuffer sbuf;
    char* s = new char[val1.size() + 1];
    std::memcpy(s, val1.c_str(), val1.size() + 1);
    msgpack::pack(sbuf, s);
    delete [] s;
    msgpack::unpacked ret;
    msgpack::unpack(ret, sbuf.data(), sbuf.size());
    EXPECT_EQ(ret.get().type, msgpack::type::STR);
    string val2 = ret.get().as<string>();
    EXPECT_EQ(val1.size(), val2.size());
    EXPECT_EQ(val1, val2);
  }
}
