// 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/reader.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filewritestream.h"

using namespace rapidjson;

static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,-1],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}";
static const char kPrettyJson[] =
  "{\n"
  "    \"hello\": \"world\",\n"
  "    \"t\": true,\n"
  "    \"f\": false,\n"
  "    \"n\": null,\n"
  "    \"i\": 123,\n"
  "    \"pi\": 3.1416,\n"
  "    \"a\": [\n"
  "        1,\n"
  "        2,\n"
  "        3,\n"
  "        -1\n"
  "    ],\n"
  "    \"u64\": 1234567890123456789,\n"
  "    \"i64\": -1234567890123456789\n"
  "}";

static const char kPrettyJson_FormatOptions_SLA[] =
  "{\n"
  "    \"hello\": \"world\",\n"
  "    \"t\": true,\n"
  "    \"f\": false,\n"
  "    \"n\": null,\n"
  "    \"i\": 123,\n"
  "    \"pi\": 3.1416,\n"
  "    \"a\": [1, 2, 3, -1],\n"
  "    \"u64\": 1234567890123456789,\n"
  "    \"i64\": -1234567890123456789\n"
  "}";

TEST(PrettyWriter, Basic)
{ StringBuffer buffer;
  PrettyWriter<StringBuffer> writer(buffer);
  Reader reader;
  StringStream s(kJson);
  reader.Parse(s, writer);
  EXPECT_STREQ(kPrettyJson, buffer.GetString());
}

TEST(PrettyWriter, FormatOptions)
{ StringBuffer buffer;
  PrettyWriter<StringBuffer> writer(buffer);
  writer.SetFormatOptions(kFormatSingleLineArray);
  Reader reader;
  StringStream s(kJson);
  reader.Parse(s, writer);
  EXPECT_STREQ(kPrettyJson_FormatOptions_SLA, buffer.GetString());
}

TEST(PrettyWriter, SetIndent)
{ StringBuffer buffer;
  PrettyWriter<StringBuffer> writer(buffer);
  writer.SetIndent('\t', 1);
  Reader reader;
  StringStream s(kJson);
  reader.Parse(s, writer);
  EXPECT_STREQ(
    "{\n"
    "\t\"hello\": \"world\",\n"
    "\t\"t\": true,\n"
    "\t\"f\": false,\n"
    "\t\"n\": null,\n"
    "\t\"i\": 123,\n"
    "\t\"pi\": 3.1416,\n"
    "\t\"a\": [\n"
    "\t\t1,\n"
    "\t\t2,\n"
    "\t\t3,\n"
    "\t\t-1\n"
    "\t],\n"
    "\t\"u64\": 1234567890123456789,\n"
    "\t\"i64\": -1234567890123456789\n"
    "}",
    buffer.GetString());
}

TEST(PrettyWriter, String)
{ StringBuffer buffer;
  PrettyWriter<StringBuffer> writer(buffer);
  EXPECT_TRUE(writer.StartArray());
  EXPECT_TRUE(writer.String("Hello\n"));
  EXPECT_TRUE(writer.EndArray());
  EXPECT_STREQ("[\n    \"Hello\\n\"\n]", buffer.GetString());
}

#if RAPIDJSON_HAS_STDSTRING
TEST(PrettyWriter, String_STDSTRING)
{ StringBuffer buffer;
  PrettyWriter<StringBuffer> writer(buffer);
  EXPECT_TRUE(writer.StartArray());
  EXPECT_TRUE(writer.String(std::string("Hello\n")));
  EXPECT_TRUE(writer.EndArray());
  EXPECT_STREQ("[\n    \"Hello\\n\"\n]", buffer.GetString());
}
#endif

#include <sstream>

class OStreamWrapper
{
public:
  typedef char Ch;

  OStreamWrapper(std::ostream& os) : os_(os) {}

  Ch Peek() const { assert(false); return '\0'; }
  Ch Take() { assert(false); return '\0'; }
  size_t Tell() const { return 0; }

  Ch* PutBegin() { assert(false); return 0; }
  void Put(Ch c) { os_.put(c); }
  void Flush() { os_.flush(); }
  size_t PutEnd(Ch*) { assert(false); return 0; }

private:
  OStreamWrapper(const OStreamWrapper&);
  OStreamWrapper& operator=(const OStreamWrapper&);

  std::ostream& os_;
};

// For covering PutN() generic version
TEST(PrettyWriter, OStreamWrapper)
{ StringStream s(kJson);

  std::stringstream ss;
  OStreamWrapper os(ss);

  PrettyWriter<OStreamWrapper> writer(os);

  Reader reader;
  reader.Parse(s, writer);

  std::string actual = ss.str();
  EXPECT_STREQ(kPrettyJson, actual.c_str());
}

// For covering FileWriteStream::PutN()
TEST(PrettyWriter, FileWriteStream)
{ char filename[L_tmpnam];
  FILE* fp = TempFile(filename);
  char buffer[16];
  FileWriteStream os(fp, buffer, sizeof(buffer));
  PrettyWriter<FileWriteStream> writer(os);
  Reader reader;
  StringStream s(kJson);
  reader.Parse(s, writer);
  fclose(fp);

  fp = fopen(filename, "rb");
  fseek(fp, 0, SEEK_END);
  size_t size = static_cast<size_t>(ftell(fp));
  fseek(fp, 0, SEEK_SET);
  char* json = static_cast<char*>(malloc(size + 1));
  size_t readLength = fread(json, 1, size, fp);
  json[readLength] = '\0';
  fclose(fp);
  remove(filename);
  EXPECT_STREQ(kPrettyJson, json);
  free(json);
}

TEST(PrettyWriter, RawValue)
{ StringBuffer buffer;
  PrettyWriter<StringBuffer> writer(buffer);
  writer.StartObject();
  writer.Key("a");
  writer.Int(1);
  writer.Key("raw");
  const char json[] = "[\"Hello\\nWorld\", 123.456]";
  writer.RawValue(json, strlen(json), kArrayType);
  writer.EndObject();
  EXPECT_TRUE(writer.IsComplete());
  EXPECT_STREQ(
    "{\n"
    "    \"a\": 1,\n"
    "    \"raw\": [\"Hello\\nWorld\", 123.456]\n" // no indentation within raw value
    "}",
    buffer.GetString());
}
