#include "UnitTest++/UnitTestPP.h"

#include "UnitTest++/MemoryOutStream.h"
#include <cstring>
#include <cstdlib>
#include <climits>
#include <cfloat>

using namespace UnitTest;
using namespace std;

namespace {

   const char* maxSignedIntegralStr(size_t nBytes)
   {
      switch(nBytes)
      {
      case 8:
         return "9223372036854775807";
      case 4:
         return "2147483647";
      case 2:
         return "32767";
      case 1:
         return "127";
      default:
         return "Unsupported signed integral size";
      }
   }

   const char* minSignedIntegralStr(size_t nBytes)
   {
      switch(nBytes)
      {
      case 8:
         return "-9223372036854775808";
      case 4:
         return "-2147483648";
      case 2:
         return "-32768";
      case 1:
         return "-128";
      default:
         return "Unsupported signed integral size";
      }
   }

   const char* maxUnsignedIntegralStr(size_t nBytes)
   {
      switch(nBytes)
      {
      case 8:
         return "18446744073709551615";
      case 4:
         return "4294967295";
      case 2:
         return "65535";
      case 1:
         return "255";
      default:
         return "Unsupported signed integral size";
      }
   }

   TEST(DefaultIsEmptyString)
   {
      MemoryOutStream const stream;
      CHECK(stream.GetText() != 0);
      CHECK_EQUAL("", stream.GetText());
   }

   TEST(StreamingTextCopiesCharacters)
   {
      MemoryOutStream stream;
      stream << "Lalala";
      CHECK_EQUAL("Lalala", stream.GetText());
   }

   TEST(StreamingMultipleTimesConcatenatesResult)
   {
      MemoryOutStream stream;
      stream << "Bork" << "Foo" << "Bar";
      CHECK_EQUAL("BorkFooBar", stream.GetText());
   }

   TEST(StreamingIntWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (int)123;
      CHECK_EQUAL("123", stream.GetText());
   }

   TEST(StreaminMaxIntWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << INT_MAX;
      CHECK_EQUAL(maxSignedIntegralStr(sizeof(int)), stream.GetText());
   }

   TEST(StreamingMinIntWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << INT_MIN;
      CHECK_EQUAL(minSignedIntegralStr(sizeof(int)), stream.GetText());
   }

   TEST(StreamingUnsignedIntWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (unsigned int)123;
      CHECK_EQUAL("123", stream.GetText());
   }

   TEST(StreamingMaxUnsignedIntWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (unsigned int)UINT_MAX;
      CHECK_EQUAL(maxUnsignedIntegralStr(sizeof(unsigned int)), stream.GetText());
   }

   TEST(StreamingMinUnsignedIntWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (unsigned int)0;
      CHECK_EQUAL("0", stream.GetText());
   }

   TEST(StreamingLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (long)(-123);
      CHECK_EQUAL("-123", stream.GetText());
   }

   TEST(StreamingMaxLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (long)(LONG_MAX);
      CHECK_EQUAL(maxSignedIntegralStr(sizeof(long)), stream.GetText());
   }

   TEST(StreamingMinLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (long)(LONG_MIN);
      CHECK_EQUAL(minSignedIntegralStr(sizeof(long)), stream.GetText());
   }

   TEST(StreamingUnsignedLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (unsigned long)123;
      CHECK_EQUAL("123", stream.GetText());
   }

   TEST(StreamingMaxUnsignedLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (unsigned long)ULONG_MAX;
      CHECK_EQUAL(maxUnsignedIntegralStr(sizeof(unsigned long)), stream.GetText());
   }

   TEST(StreamingMinUnsignedLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (unsigned long)0ul;
      CHECK_EQUAL("0", stream.GetText());
   }

   TEST(StreamingLongLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
#ifdef UNITTEST_COMPILER_IS_MSVC6
      stream << (__int64)-12345i64;
#else
      stream << (long long)-12345ll;
#endif
      CHECK_EQUAL("-12345", stream.GetText());
   }

#ifdef LLONG_MAX
   TEST(StreamingMaxLongLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (long long)LLONG_MAX;
      CHECK_EQUAL(maxSignedIntegralStr(sizeof(long long)), stream.GetText());
   }
#endif

#ifdef LLONG_MIN
   TEST(StreamingMinLongLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (long long)LLONG_MIN;
      CHECK_EQUAL(minSignedIntegralStr(sizeof(long long)), stream.GetText());
   }
#endif

   TEST(StreamingUnsignedLongLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
#ifdef UNITTEST_COMPILER_IS_MSVC6
      stream << (unsigned __int64)85899ui64;
#else
      stream << (unsigned long long)85899ull;
#endif
      CHECK_EQUAL("85899", stream.GetText());
   }

#ifdef ULLONG_MAX
   TEST(StreamingMaxUnsignedLongLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << (unsigned long long)ULLONG_MAX;
      CHECK_EQUAL(maxUnsignedIntegralStr(sizeof(unsigned long long)), stream.GetText());
   }
#endif

   TEST(StreamingMinUnsignedLongLongWritesCorrectCharacters)
   {
      MemoryOutStream stream;
#ifdef UNITTEST_COMPILER_IS_MSVC6
      stream << (unsigned __int64)0ui64;
#else
      stream << (unsigned long long)0ull;
#endif
      CHECK_EQUAL("0", stream.GetText());
   }

   TEST(StreamingFloatWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << 3.1415f;
      CHECK(strstr(stream.GetText(), "3.1415"));
   }

   TEST(StreamingDoubleWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      stream << 3.1415;
      CHECK(strstr(stream.GetText(), "3.1415"));
   }

   TEST(StreamingPointerWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      int* p = (int*)0x1234;
      stream << p;
      CHECK(strstr(stream.GetText(), "1234"));
   }

   TEST(StreamingSizeTWritesCorrectCharacters)
   {
      MemoryOutStream stream;
      size_t const s = 53124;
      stream << s;
      CHECK_EQUAL("53124", stream.GetText());
   }

   TEST(ClearEmptiesMemoryOutStreamContents)
   {
      MemoryOutStream stream;
      stream << "Hello world";
      stream.Clear();
      CHECK_EQUAL("", stream.GetText());
   }

#ifndef UNITTEST_MEMORYOUTSTREAM_IS_STD_OSTRINGSTREAM

   TEST(StreamInitialCapacityIsCorrect)
   {
      MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE);
      CHECK_EQUAL((int)MemoryOutStream::GROW_CHUNK_SIZE, stream.GetCapacity());
   }

   TEST(StreamInitialCapacityIsMultipleOfGrowChunkSize)
   {
      MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE + 1);
      CHECK_EQUAL((int)MemoryOutStream::GROW_CHUNK_SIZE * 2, stream.GetCapacity());
   }


   TEST(ExceedingCapacityGrowsBuffer)
   {
      MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE);
      stream << "012345678901234567890123456789";
      char const* const oldBuffer = stream.GetText();
      stream << "0123456789";
      CHECK(oldBuffer != stream.GetText());
   }

   TEST(ExceedingCapacityGrowsBufferByGrowChunk)
   {
      MemoryOutStream stream(MemoryOutStream::GROW_CHUNK_SIZE);
      stream << "0123456789012345678901234567890123456789";
      CHECK_EQUAL(MemoryOutStream::GROW_CHUNK_SIZE * 2, stream.GetCapacity());
   }

   TEST(WritingStringLongerThanCapacityFitsInNewBuffer)
   {
      MemoryOutStream stream(8);
      stream << "0123456789ABCDEF";
      CHECK_EQUAL("0123456789ABCDEF", stream.GetText());
   }

   TEST(WritingIntLongerThanCapacityFitsInNewBuffer)
   {
      MemoryOutStream stream(8);
      stream << "aaaa" << 123456;
      CHECK_EQUAL("aaaa123456", stream.GetText());
   }

   TEST(WritingFloatLongerThanCapacityFitsInNewBuffer)
   {
      MemoryOutStream stream(8);
      stream << "aaaa" << 123456.0f;
      CHECK_EQUAL("aaaa123456.000000", stream.GetText());
   }

   TEST(WritingSizeTLongerThanCapacityFitsInNewBuffer)
   {
      MemoryOutStream stream(8);
      stream << "aaaa" << size_t(32145);
      CHECK_EQUAL("aaaa32145", stream.GetText());
   }

   TEST(VerifyLargeDoubleCanBeStreamedWithoutCrashing)
   {
      MemoryOutStream stream(8);
      stream << DBL_MAX;
      CHECK(true);
   }

#endif

}
