// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#include "unittest.h"

#include "rapidjson/istreamwrapper.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/document.h"
#include <sstream>
#include <fstream>

#ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4702) // unreachable code
#endif

using namespace rapidjson;
using namespace std;

template <typename StringStreamType>
static void TestStringStream()
{ typedef typename StringStreamType::char_type Ch;

  { StringStreamType iss;
    BasicIStreamWrapper<StringStreamType> is(iss);
    EXPECT_EQ(0, is.Tell());
    if (sizeof(Ch) == 1)
    { EXPECT_EQ(0, is.Peek4());
      EXPECT_EQ(0, is.Tell());
    }
    EXPECT_EQ(0, is.Peek());
    EXPECT_EQ(0, is.Take());
    EXPECT_EQ(0, is.Tell());
  }

  { Ch s[] = { 'A', 'B', 'C', '\0' };
    StringStreamType iss(s);
    BasicIStreamWrapper<StringStreamType> is(iss);
    EXPECT_EQ(0, is.Tell());
    if (sizeof(Ch) == 1)
    { EXPECT_EQ(0, is.Peek4()); // less than 4 bytes
    }
    for (int i = 0; i < 3; i++)
    { EXPECT_EQ(static_cast<size_t>(i), is.Tell());
      EXPECT_EQ('A' + i, is.Peek());
      EXPECT_EQ('A' + i, is.Peek());
      EXPECT_EQ('A' + i, is.Take());
    }
    EXPECT_EQ(3, is.Tell());
    EXPECT_EQ(0, is.Peek());
    EXPECT_EQ(0, is.Take());
  }

  { Ch s[] = { 'A', 'B', 'C', 'D', 'E', '\0' };
    StringStreamType iss(s);
    BasicIStreamWrapper<StringStreamType> is(iss);
    if (sizeof(Ch) == 1)
    { const Ch* c = is.Peek4();
      for (int i = 0; i < 4; i++)
        EXPECT_EQ('A' + i, c[i]);
      EXPECT_EQ(0, is.Tell());
    }
    for (int i = 0; i < 5; i++)
    { EXPECT_EQ(static_cast<size_t>(i), is.Tell());
      EXPECT_EQ('A' + i, is.Peek());
      EXPECT_EQ('A' + i, is.Peek());
      EXPECT_EQ('A' + i, is.Take());
    }
    EXPECT_EQ(5, is.Tell());
    EXPECT_EQ(0, is.Peek());
    EXPECT_EQ(0, is.Take());
  }
}

TEST(IStreamWrapper, istringstream)
{ TestStringStream<istringstream>();
}

TEST(IStreamWrapper, stringstream)
{ TestStringStream<stringstream>();
}

TEST(IStreamWrapper, wistringstream)
{ TestStringStream<wistringstream>();
}

TEST(IStreamWrapper, wstringstream)
{ TestStringStream<wstringstream>();
}

template <typename FileStreamType>
static bool Open(FileStreamType& fs, const char* filename)
{ const char *paths[] =
  { "encodings",
    "bin/encodings",
    "../bin/encodings",
    "../../bin/encodings",
    "../../../bin/encodings"
  };
  char buffer[1024];
  for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++)
  { sprintf(buffer, "%s/%s", paths[i], filename);
    fs.open(buffer, ios_base::in | ios_base::binary);
    if (fs.is_open())
      return true;
  }
  return false;
}

TEST(IStreamWrapper, ifstream)
{ ifstream ifs;
  ASSERT_TRUE(Open(ifs, "utf8bom.json"));
  IStreamWrapper isw(ifs);
  EncodedInputStream<UTF8<>, IStreamWrapper> eis(isw);
  Document d;
  EXPECT_TRUE(!d.ParseStream(eis).HasParseError());
  EXPECT_TRUE(d.IsObject());
  EXPECT_EQ(5, d.MemberCount());
}

TEST(IStreamWrapper, fstream)
{ fstream fs;
  ASSERT_TRUE(Open(fs, "utf8bom.json"));
  IStreamWrapper isw(fs);
  EncodedInputStream<UTF8<>, IStreamWrapper> eis(isw);
  Document d;
  EXPECT_TRUE(!d.ParseStream(eis).HasParseError());
  EXPECT_TRUE(d.IsObject());
  EXPECT_EQ(5, d.MemberCount());
}

// wifstream/wfstream only works on C++11 with codecvt_utf16
// But many C++11 library still not have it.
#if 0
#include <codecvt>

TEST(IStreamWrapper, wifstream)
{ wifstream ifs;
  ASSERT_TRUE(Open(ifs, "utf16bebom.json"));
  ifs.imbue(std::locale(ifs.getloc(),
                        new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
  WIStreamWrapper isw(ifs);
  GenericDocument<UTF16<> > d;
  d.ParseStream<kParseDefaultFlags, UTF16<>, WIStreamWrapper>(isw);
  EXPECT_TRUE(!d.HasParseError());
  EXPECT_TRUE(d.IsObject());
  EXPECT_EQ(5, d.MemberCount());
}

TEST(IStreamWrapper, wfstream)
{ wfstream fs;
  ASSERT_TRUE(Open(fs, "utf16bebom.json"));
  fs.imbue(std::locale(fs.getloc(),
                       new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
  WIStreamWrapper isw(fs);
  GenericDocument<UTF16<> > d;
  d.ParseStream<kParseDefaultFlags, UTF16<>, WIStreamWrapper>(isw);
  EXPECT_TRUE(!d.HasParseError());
  EXPECT_TRUE(d.IsObject());
  EXPECT_EQ(5, d.MemberCount());
}

#endif

#ifdef _MSC_VER
RAPIDJSON_DIAG_POP
#endif
