#pragma once

#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <vector>
#include <string>

#include "optional.h"

using std::experimental::optional;
using std::experimental::nullopt;

using Reader = rapidjson::GenericValue<rapidjson::UTF8<>>;
using Writer = rapidjson::PrettyWriter<rapidjson::StringBuffer>;
struct IndexedFile;


#define REFLECT_MEMBER_START() \
    if (!ReflectMemberStart(visitor, value)) return
#define REFLECT_MEMBER_START1(value) \
    if (!ReflectMemberStart(visitor, value)) return
#define REFLECT_MEMBER_END() \
    ReflectMemberEnd(visitor, value);
#define REFLECT_MEMBER_END1(value) \
    ReflectMemberEnd(visitor, value);
#define REFLECT_MEMBER(name) \
    ReflectMember(visitor, #name, value.name)
#define REFLECT_MEMBER2(name, value) \
    ReflectMember(visitor, name, value)


// API:
/*
template<typename TVisitor, typename T>
void Reflect(TVisitor& visitor, T& value) {
  static_assert(false, "Missing implementation");
}
template<typename TVisitor>
void DefaultReflectMemberStart(TVisitor& visitor) {
  static_assert(false, "Missing implementation");
}
template<typename TVisitor, typename T>
bool ReflectMemberStart(TVisitor& visitor, T& value) {
  static_assert(false, "Missing implementation");
  return true;
}
template<typename TVisitor, typename T>
void ReflectMemberEnd(TVisitor& visitor, T& value) {
  static_assert(false, "Missing implementation");
}
*/



// int
void Reflect(Reader& visitor, int& value);
void Reflect(Writer& visitor, int& value);
// bool
void Reflect(Reader& visitor, bool& value);
void Reflect(Writer& visitor, bool& value);
// std::string
void Reflect(Reader& visitor, std::string& value);
void Reflect(Writer& visitor, std::string& value);







// Writer:
template<typename T>
void Reflect(Writer& visitor, std::vector<T>& values) {
  visitor.StartArray();
  for (auto& value : values)
    Reflect(visitor, value);
  visitor.EndArray();
}
template<typename T>
void Reflect(Writer& visitor, optional<T> value) {
  if (value)
    Reflect(visitor, value.value());
}
inline void DefaultReflectMemberStart(Writer& visitor) {
  visitor.StartObject();
}
template<typename T>
bool ReflectMemberStart(Writer& visitor, T& value) {
  visitor.StartObject();
  return true;
}
template<typename T>
void ReflectMemberEnd(Writer& visitor, T& value) {
  visitor.EndObject();
}
template<typename T>
void ReflectMember(Writer& visitor, const char* name, T& value) {
  visitor.Key(name);
  Reflect(visitor, value);
}
template<typename T>
void ReflectMember(Writer& visitor, const char* name, std::vector<T>& values) {
  if (values.empty())
    return;
  visitor.Key(name);
  visitor.StartArray();
  for (auto& value : values)
    Reflect(visitor, value);
  visitor.EndArray();
}
template<typename T>
void ReflectMember(Writer& visitor, const char* name, optional<T>& value) {
  if (!value)
    return;
  visitor.Key(name);
  Reflect(visitor, value);
}
void ReflectMember(Writer& visitor, const char* name, std::string& value);

// Reader:
template<typename T>
void Reflect(Reader& visitor, std::vector<T>& values) {
  for (auto& entry : visitor.GetArray()) {
    T entry_value;
    Reflect(entry, entry_value);
    values.push_back(entry_value);
  }
}
template<typename T>
void Reflect(Reader& visitor, optional<T>& value) {
  T real_value;
  Reflect(visitor, real_value);
  value = real_value;
}
inline void DefaultReflectMemberStart(Reader& visitor) {}
template<typename T>
bool ReflectMemberStart(Reader& visitor, T& value) {
  return true;
}
template<typename T>
void ReflectMemberEnd(Reader& visitor, T& value) {}
template<typename T>
void ReflectMember(Reader& visitor, const char* name, T& value) {
  auto it = visitor.FindMember(name);
  if (it != visitor.MemberEnd()) {
    Reader& child_visitor = it->value;
    Reflect(child_visitor, value);
  }
}














#if false

void Serialize(Writer& writer, int value);
void Serialize(Writer& writer, const std::string& value);
void Serialize(Writer& writer, Location location);
void Serialize(Writer& writer, uint64_t value);
void Serialize(Writer& writer, IndexedFile& file);

template<typename T>
void Serialize(Writer& writer, Id<T> id) {
  writer.Uint64(id.id);
}

template<typename T>
void Serialize(Writer& writer, optional<T> value) {
  if (value)
    Serialize(writer, value.value());
  else
    writer.Null();
}

template<typename T>
void Serialize(Writer& writer, const std::vector<T>& values) {
  writer.StartArray();
  for (const T& value : values)
    Serialize(writer, value);
  writer.EndArray();
}

template<typename T>
void Serialize(Writer& writer, Ref<T> ref) {
  std::string s = std::to_string(ref.id.id) + "@" + ref.loc.ToString();
  writer.String(s.c_str());
}

template<typename T>
void Serialize(Writer& writer, const char* key, const std::vector<Ref<T>>& refs) {
  if (refs.size() == 0)
    return;

  if (key) writer.Key(key);
  writer.StartArray();
  for (Ref<T> ref : refs)
    Serialize(writer, nullptr, ref);
  writer.EndArray();
}





#define SERIALIZE_MEMBER(name) \
    SerializeMember(writer, #name, value.name)
#define SERIALIZE_MEMBER2(name, value) \
    SerializeMember(writer, name, value)
#define DESERIALIZE_MEMBER(name) \
    DeserializeMember(reader, #name, value.name)
#define DESERIALIZE_MEMBER2(name, value) \
    DeserializeMember(reader, name, value)

// Special templates used by (DE)SERIALIZE_MEMBER macros.
template<typename T>
void SerializeMember(Writer& writer, const char* name, const T& value) {
  writer.Key(name);
  Serialize(writer, value);
}
template<typename T>
void SerializeMember(Writer& writer, const char* name, const std::vector<T>& value) {
  if (value.empty())
    return;
  writer.Key(name);
  Serialize(writer, value);
}
template<typename T>
void SerializeMember(Writer& writer, const char* name, const optional<T>& value) {
  if (!value)
    return;
  writer.Key(name);
  Serialize(writer, value.value());
}

void SerializeMember(Writer& writer, const char* name, const std::string& value);

template<typename T>
void DeserializeMember(const Reader& reader, const char* name, T& value) {
  auto it = reader.FindMember(name);
  if (it != reader.MemberEnd())
    Deserialize(it->value, value);
}







template<typename T>
void Deserialize(const Reader& reader, Id<T>& output) {
  output = Id<T>(reader.GetUint64());
}

template<typename T>
void Deserialize(const Reader& reader, Ref<T>& output) {
  const char* str_value = reader.GetString();
  uint64_t id = atoi(str_value);
  const char* loc_string = strchr(str_value, '@') + 1;

  output.id = Id<T>(id);
  output.loc = Location(loc_string);
}

template<typename T>
void Deserialize(const Reader& reader, std::vector<T>& value) {
  for (const auto& entry : reader.GetArray()) {
    T entry_value;
    Deserialize(entry, entry_value);
    value.push_back(entry_value);
  }
}

template<typename T>
void Deserialize(const Reader& reader, optional<T>& value) {
  T real_value;
  Deserialize(reader, real_value);
  value = real_value;
}

void Deserialize(const Reader& reader, int& value);
void Deserialize(const Reader& reader, bool& value);
void Deserialize(const Reader& reader, std::string& value);
void Deserialize(const Reader& reader, Location& output);
void Deserialize(const Reader& reader, IndexedTypeDef& value);
void Deserialize(const Reader& reader, IndexedFuncDef& value);
void Deserialize(const Reader& reader, IndexedVarDef& value);
void Deserialize(const Reader& reader, IndexedFile& file);

#endif

std::string Serialize(IndexedFile& file);
IndexedFile Deserialize(std::string path, std::string serialized);