#ifndef TINYTOML_H_
#define TINYTOML_H_

#include <algorithm>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <istream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <map>
#include <memory>
#include <utility>
#include <vector>

namespace toml {

// ----------------------------------------------------------------------
// Declarations

class Value;
typedef std::chrono::system_clock::time_point Time;
typedef std::vector<Value> Array;
typedef std::map<std::string, Value> Table;

namespace internal {
template <typename T>
struct call_traits_value {
  typedef T return_type;
};
template <typename T>
struct call_traits_ref {
  typedef const T& return_type;
};
}  // namespace internal

template <typename T>
struct call_traits;
template <>
struct call_traits<bool> : public internal::call_traits_value<bool> {};
template <>
struct call_traits<int> : public internal::call_traits_value<int> {};
template <>
struct call_traits<int64_t> : public internal::call_traits_value<int64_t> {};
template <>
struct call_traits<double> : public internal::call_traits_value<double> {};
template <>
struct call_traits<std::string> : public internal::call_traits_ref<std::string> {};
template <>
struct call_traits<Time> : public internal::call_traits_ref<Time> {};
template <>
struct call_traits<Array> : public internal::call_traits_ref<Array> {};
template <>
struct call_traits<Table> : public internal::call_traits_ref<Table> {};

// A value is returned for std::vector<T>. Not reference.
// This is because a fresh vector is made.
template <typename T>
struct call_traits<std::vector<T>> : public internal::call_traits_value<std::vector<T>> {};

// Formatting flags
enum FormatFlag { FORMAT_NONE = 0, FORMAT_INDENT = 1 };

class Value {
 public:
  enum Type {
    NULL_TYPE,
    BOOL_TYPE,
    INT_TYPE,
    DOUBLE_TYPE,
    STRING_TYPE,
    TIME_TYPE,
    ARRAY_TYPE,
    TABLE_TYPE,
  };

  Value() : type_(NULL_TYPE), null_(nullptr) {}
  Value(bool v) : type_(BOOL_TYPE), bool_(v) {}
  Value(int v) : type_(INT_TYPE), int_(v) {}
  Value(int64_t v) : type_(INT_TYPE), int_(v) {}
  Value(double v) : type_(DOUBLE_TYPE), double_(v) {}
  Value(const std::string& v) : type_(STRING_TYPE), string_(new std::string(v)) {}
  Value(const char* v) : type_(STRING_TYPE), string_(new std::string(v)) {}
  Value(const Time& v) : type_(TIME_TYPE), time_(new Time(v)) {}
  Value(const Array& v) : type_(ARRAY_TYPE), array_(new Array(v)) {}
  Value(const Table& v) : type_(TABLE_TYPE), table_(new Table(v)) {}
  Value(std::string&& v) : type_(STRING_TYPE), string_(new std::string(std::move(v))) {}
  Value(Array&& v) : type_(ARRAY_TYPE), array_(new Array(std::move(v))) {}
  Value(Table&& v) : type_(TABLE_TYPE), table_(new Table(std::move(v))) {}

  Value(const Value& v);
  Value(Value&& v) noexcept;
  Value& operator=(const Value& v);
  Value& operator=(Value&& v) noexcept;

  // Guards from unexpected Value construction.
  // Someone might use a value like this:
  //   toml::Value v = x->find("foo");
  // But this is wrong. Without this constructor,
  // value will be unexpectedly initialized with bool.
  Value(const void* v) = delete;
  ~Value();

  // Retruns Value size.
  // 0 for invalid value.
  // The number of inner elements for array or table.
  // 1 for other types.
  size_t size() const;
  bool empty() const;
  Type type() const { return type_; }

  bool valid() const { return type_ != NULL_TYPE; }
  template <typename T>
  bool is() const;
  template <typename T>
  typename call_traits<T>::return_type as() const;

  friend bool operator==(const Value& lhs, const Value& rhs);
  friend bool operator!=(const Value& lhs, const Value& rhs) { return !(lhs == rhs); }

  // ----------------------------------------------------------------------
  // For integer/floating value

  // Returns true if the value is int or double.
  bool isNumber() const;
  // Returns number. Convert to double.
  double asNumber() const;

  // ----------------------------------------------------------------------
  // For Time value

  // Converts to time_t if the internal value is Time.
  // We don't have as<std::time_t>(). Since time_t is basically signed long,
  // it's something like a method to converting to (normal) integer.
  std::time_t as_time_t() const;

  // ----------------------------------------------------------------------
  // For Table value
  template <typename T>
  typename call_traits<T>::return_type get(const std::string&) const;
  Value* set(const std::string& key, const Value& v);
  // Finds a Value with |key|. |key| can contain '.'
  // Note: if you would like to find a child value only, you need to use findChild.
  const Value* find(const std::string& key) const;
  Value* find(const std::string& key);
  bool has(const std::string& key) const { return find(key) != nullptr; }
  bool erase(const std::string& key);

  Value& operator[](const std::string& key);

  // Merge table. Returns true if succeeded. Otherwise, |this| might be corrupted.
  // When the same key exists, it will be overwritten.
  bool merge(const Value&);

  // Finds a value with |key|. It searches only children.
  Value* findChild(const std::string& key);
  const Value* findChild(const std::string& key) const;
  // Sets a value, and returns the pointer to the created value.
  // When the value having the same key exists, it will be overwritten.
  Value* setChild(const std::string& key, const Value& v);
  Value* setChild(const std::string& key, Value&& v);
  bool eraseChild(const std::string& key);

  // ----------------------------------------------------------------------
  // For Array value

  template <typename T>
  typename call_traits<T>::return_type get(size_t index) const;
  const Value* find(size_t index) const;
  Value* find(size_t index);
  Value* push(const Value& v);
  Value* push(Value&& v);

  // ----------------------------------------------------------------------
  // Others

  // Writer.
  static std::string spaces(int num);
  static std::string escapeKey(const std::string& key);

  void write(std::ostream*, const std::string& keyPrefix = std::string(), int indent = -1) const;
  void writeFormatted(std::ostream*, FormatFlag flags) const;

  friend std::ostream& operator<<(std::ostream&, const Value&);

 private:
  static const char* typeToString(Type);

  template <typename T>
  void assureType() const;
  Value* ensureValue(const std::string& key);

  template <typename T>
  struct ValueConverter;

  Type type_;
  union {
    void* null_;
    bool bool_;
    int64_t int_;
    double double_;
    std::string* string_;
    Time* time_;
    Array* array_;
    Table* table_;
  };

  template <typename T>
  friend struct ValueConverter;
};

// parse() returns ParseResult.
struct ParseResult {
  ParseResult(toml::Value v, std::string er) : value(std::move(v)), errorReason(std::move(er)) {}

  bool valid() const { return value.valid(); }

  toml::Value value;
  std::string errorReason;
};

// Parses from std::istream.
ParseResult parse(std::istream&);
// Parses a file.
ParseResult parseFile(const std::string& filename);

// ----------------------------------------------------------------------
// Declarations for Implementations
//   You don't need to understand the below to use this library.

#if defined(_WIN32)
// Windows does not have timegm but have _mkgmtime.
inline time_t timegm(std::tm* timeptr) { return _mkgmtime(timeptr); }

// On Windows, Visual Studio does not define gmtime_r. However, mingw might
// do (or might not do). See https://github.com/mayah/tinytoml/issues/25,
#ifndef gmtime_r
inline struct tm* gmtime_r(const time_t* t, struct tm* r) {
  // gmtime is threadsafe in windows because it uses TLS
  struct tm* theTm = gmtime(t);
  if (theTm) {
    *r = *theTm;
    return r;
  } else {
    return 0;
  }
}
#endif  // gmtime_r
#endif  // _WIN32

namespace internal {

enum class TokenType {
  ERROR_TOKEN,
  END_OF_FILE,
  END_OF_LINE,
  IDENT,
  STRING,
  MULTILINE_STRING,
  BOOL,
  INT,
  DOUBLE,
  TIME,
  COMMA,
  DOT,
  EQUAL,
  LBRACKET,
  RBRACKET,
  LBRACE,
  RBRACE,
};

class Token {
 public:
  explicit Token(TokenType tokenType) : type_(tokenType) {}
  Token(TokenType tokenType, const std::string& v) : type_(tokenType), str_value_(v) {}
  Token(TokenType tokenType, bool v) : type_(tokenType), int_value_(v) {}
  Token(TokenType tokenType, std::int64_t v) : type_(tokenType), int_value_(v) {}
  Token(TokenType tokenType, double v) : type_(tokenType), double_value_(v) {}
  Token(TokenType tokenType, std::chrono::system_clock::time_point tp)
      : type_(tokenType), time_value_(tp) {}

  TokenType type() const { return type_; }
  const std::string& strValue() const { return str_value_; }
  bool boolValue() const { return int_value_ != 0; }
  std::int64_t intValue() const { return int_value_; }
  double doubleValue() const { return double_value_; }
  std::chrono::system_clock::time_point timeValue() const { return time_value_; }

 private:
  TokenType type_;
  std::string str_value_;
  std::int64_t int_value_;
  double double_value_;
  std::chrono::system_clock::time_point time_value_;
};

class Lexer {
 public:
  explicit Lexer(std::istream& is) : is_(is), lineNo_(1) {}

  Token nextKeyToken();
  Token nextValueToken();

  int lineNo() const { return lineNo_; }

  // Skips if UTF8BOM is found.
  // Returns true if success. Returns false if intermediate state is left.
  bool skipUTF8BOM();

 private:
  bool current(char* c);
  void next();
  bool consume(char c);

  Token nextToken(bool isValueToken);

  void skipUntilNewLine();

  Token nextStringDoubleQuote();
  Token nextStringSingleQuote();

  Token nextKey();
  Token nextValue();

  Token parseAsTime(const std::string&);

  std::istream& is_;
  int lineNo_;
};

class Parser {
 public:
  explicit Parser(std::istream& is) : lexer_(is), token_(TokenType::ERROR_TOKEN) {
    if (!lexer_.skipUTF8BOM()) {
      token_ = Token(TokenType::ERROR_TOKEN, std::string("Invalid UTF8 BOM"));
    } else {
      nextKey();
    }
  }

  // Parses. If failed, value should be invalid value.
  // You can get the error by calling errorReason().
  Value parse();
  const std::string& errorReason();

 private:
  const Token& token() const { return token_; }
  void nextKey() { token_ = lexer_.nextKeyToken(); }
  void nextValue() { token_ = lexer_.nextValueToken(); }

  void skipForKey();
  void skipForValue();

  bool consumeForKey(TokenType);
  bool consumeForValue(TokenType);
  bool consumeEOLorEOFForKey();

  Value* parseGroupKey(Value* root);

  bool parseKeyValue(Value*);
  bool parseKey(std::string*);
  bool parseValue(Value*);
  bool parseBool(Value*);
  bool parseNumber(Value*);
  bool parseArray(Value*);
  bool parseInlineTable(Value*);

  void addError(const std::string& reason);

  Lexer lexer_;
  Token token_;
  std::string errorReason_;
};

}  // namespace internal

// ----------------------------------------------------------------------
// Implementations

inline ParseResult parse(std::istream& is) {
  if (!is) {
    return ParseResult(toml::Value(), "stream is in bad state. file does not exist?");
  }

  internal::Parser parser(is);
  toml::Value v = parser.parse();

  if (v.valid()) return ParseResult(std::move(v), std::string());

  return ParseResult(std::move(v), std::move(parser.errorReason()));
}

inline ParseResult parseFile(const std::string& filename) {
  std::ifstream ifs(filename);
  if (!ifs) {
    return ParseResult(toml::Value(), std::string("could not open file: ") + filename);
  }

  return parse(ifs);
}

inline std::string format(std::stringstream& ss) { return ss.str(); }

template <typename T, typename... Args>
std::string format(std::stringstream& ss, T&& t, Args&&... args) {
  ss << std::forward<T>(t);
  return format(ss, std::forward<Args>(args)...);
}

// If you want to compile without exception,
//   1. Define TOML_HAVE_FAILWITH_REPLACEMENT
//   2. Define your own toml::failwith.
// e.g. You can just abort here instead of exception.
#ifndef TOML_HAVE_FAILWITH_REPLACEMENT

template<typename... Args>
#if defined(_MSC_VER)
__declspec(noreturn)
#else
[[noreturn]]
#endif
void failwith(Args&&... args)
{
  std::stringstream ss;
  throw std::runtime_error(format(ss, std::forward<Args>(args)...));
}

#endif

namespace internal {

inline std::string removeDelimiter(const std::string& s) {
  std::string r;
  for (char c : s) {
    if (c == '_') continue;
    r += c;
  }
  return r;
}

inline std::string unescape(const std::string& codepoint) {
  std::uint32_t x;
  std::uint8_t buf[8];
  std::stringstream ss(codepoint);

  ss >> std::hex >> x;

  if (x <= 0x7FUL) {
    // 0xxxxxxx
    buf[0] = 0x00 | ((x >> 0) & 0x7F);
    buf[1] = '\0';
  } else if (x <= 0x7FFUL) {
    // 110yyyyx 10xxxxxx
    buf[0] = 0xC0 | ((x >> 6) & 0xDF);
    buf[1] = 0x80 | ((x >> 0) & 0xBF);
    buf[2] = '\0';
  } else if (x <= 0xFFFFUL) {
    // 1110yyyy 10yxxxxx 10xxxxxx
    buf[0] = 0xE0 | ((x >> 12) & 0xEF);
    buf[1] = 0x80 | ((x >> 6) & 0xBF);
    buf[2] = 0x80 | ((x >> 0) & 0xBF);
    buf[3] = '\0';
  } else if (x <= 0x10FFFFUL) {
    // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
    buf[0] = 0xF0 | ((x >> 18) & 0xF7);
    buf[1] = 0x80 | ((x >> 12) & 0xBF);
    buf[2] = 0x80 | ((x >> 6) & 0xBF);
    buf[3] = 0x80 | ((x >> 0) & 0xBF);
    buf[4] = '\0';
  } else {
    buf[0] = '\0';
  }

  return reinterpret_cast<char*>(buf);
}

// Returns true if |s| is integer.
// [+-]?\d+(_\d+)*
inline bool isInteger(const std::string& s) {
  if (s.empty()) return false;

  std::string::size_type p = 0;
  if (s[p] == '+' || s[p] == '-') ++p;

  while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
    ++p;
    if (p < s.size() && s[p] == '_') {
      ++p;
      if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) return false;
    }
  }

  return p == s.size();
}

// Returns true if |s| is double.
// [+-]? (\d+(_\d+)*)? (\.\d+(_\d+)*)? ([eE] [+-]? \d+(_\d+)*)?
//       1-----------  2-------------  3----------------------
// 2 or (1 and 3) should exist.
inline bool isDouble(const std::string& s) {
  if (s.empty()) return false;

  std::string::size_type p = 0;
  if (s[p] == '+' || s[p] == '-') ++p;

  bool ok = false;
  while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
    ++p;
    ok = true;

    if (p < s.size() && s[p] == '_') {
      ++p;
      if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) return false;
    }
  }

  if (p < s.size() && s[p] == '.') ++p;

  while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
    ++p;
    ok = true;

    if (p < s.size() && s[p] == '_') {
      ++p;
      if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) return false;
    }
  }

  if (!ok) return false;

  ok = false;
  if (p < s.size() && (s[p] == 'e' || s[p] == 'E')) {
    ++p;
    if (p < s.size() && (s[p] == '+' || s[p] == '-')) ++p;
    while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
      ++p;
      ok = true;

      if (p < s.size() && s[p] == '_') {
        ++p;
        if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) return false;
      }
    }
    if (!ok) return false;
  }

  return p == s.size();
}

// static
inline std::string escapeString(const std::string& s) {
  std::stringstream ss;
  for (size_t i = 0; i < s.size(); ++i) {
    switch (s[i]) {
      case '\n':
        ss << "\\n";
        break;
      case '\r':
        ss << "\\r";
        break;
      case '\t':
        ss << "\\t";
        break;
      case '\"':
        ss << "\\\"";
        break;
      case '\'':
        ss << "\\\'";
        break;
      case '\\':
        ss << "\\\\";
        break;
      default:
        ss << s[i];
        break;
    }
  }

  return ss.str();
}

}  // namespace internal

// ----------------------------------------------------------------------
// Lexer

namespace internal {

inline bool Lexer::skipUTF8BOM() {
  // Check [EF, BB, BF]

  int x1 = is_.peek();
  if (x1 != 0xEF) {
    // When the first byte is not 0xEF, it's not UTF8 BOM.
    // Just return true.
    return true;
  }

  is_.get();
  int x2 = is_.get();
  if (x2 != 0xBB) {
    return false;
  }

  int x3 = is_.get();
  if (x3 != 0xBF) {
    return false;
  }

  return true;
}

inline bool Lexer::current(char* c) {
  int x = is_.peek();
  if (x == EOF) return false;
  *c = static_cast<char>(x);
  return true;
}

inline void Lexer::next() {
  int x = is_.get();
  if (x == '\n') ++lineNo_;
}

inline bool Lexer::consume(char c) {
  char x;
  if (!current(&x)) return false;
  if (x != c) return false;
  next();
  return true;
}

inline void Lexer::skipUntilNewLine() {
  char c;
  while (current(&c)) {
    if (c == '\n') return;
    next();
  }
}

inline Token Lexer::nextStringDoubleQuote() {
  if (!consume('"'))
    return Token(TokenType::ERROR_TOKEN, std::string("string didn't start with '\"'"));

  std::string s;
  char c;
  bool multiline = false;

  if (current(&c) && c == '"') {
    next();
    if (!current(&c) || c != '"') {
      // OK. It's empty string.
      return Token(TokenType::STRING, std::string());
    }

    next();
    // raw string literal started.
    // Newline just after """ should be ignored.
    while (current(&c) && (c == ' ' || c == '\t')) next();
    if (current(&c) && c == '\n') next();
    multiline = true;
  }

  while (current(&c)) {
    next();
    if (c == '\\') {
      if (!current(&c))
        return Token(TokenType::ERROR_TOKEN, std::string("string has unknown escape sequence"));
      next();
      switch (c) {
        case 't':
          c = '\t';
          break;
        case 'n':
          c = '\n';
          break;
        case 'r':
          c = '\r';
          break;
        case 'u':
        case 'U': {
          int size = c == 'u' ? 4 : 8;
          std::string codepoint;
          for (int i = 0; i < size; ++i) {
            if (current(&c) &&
                (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))) {
              codepoint += c;
              next();
            } else {
              return Token(TokenType::ERROR_TOKEN,
                           std::string("string has unknown escape sequence"));
            }
          }
          s += unescape(codepoint);
          continue;
        }
        case '"':
          c = '"';
          break;
        case '\'':
          c = '\'';
          break;
        case '\\':
          c = '\\';
          break;
        case '\n':
          while (current(&c) && (c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
            next();
          }
          continue;
        default:
          return Token(TokenType::ERROR_TOKEN, std::string("string has unknown escape sequence"));
      }
    } else if (c == '"') {
      if (multiline) {
        if (current(&c) && c == '"') {
          next();
          if (current(&c) && c == '"') {
            next();
            return Token(TokenType::MULTILINE_STRING, s);
          } else {
            s += '"';
            s += '"';
            continue;
          }
        } else {
          s += '"';
          continue;
        }
      } else {
        return Token(TokenType::STRING, s);
      }
    }

    s += c;
  }

  return Token(TokenType::ERROR_TOKEN, std::string("string didn't end"));
}

inline Token Lexer::nextStringSingleQuote() {
  if (!consume('\''))
    return Token(TokenType::ERROR_TOKEN, std::string("string didn't start with '\''?"));

  std::string s;
  char c;

  if (current(&c) && c == '\'') {
    next();
    if (!current(&c) || c != '\'') {
      // OK. It's empty string.
      return Token(TokenType::STRING, std::string());
    }
    next();
    // raw string literal started.
    // Newline just after """ should be ignored.
    if (current(&c) && c == '\n') next();

    while (current(&c)) {
      if (c == '\'') {
        next();
        if (current(&c) && c == '\'') {
          next();
          if (current(&c) && c == '\'') {
            next();
            return Token(TokenType::MULTILINE_STRING, s);
          } else {
            s += '\'';
            s += '\'';
            continue;
          }
        } else {
          s += '\'';
          continue;
        }
      }

      next();
      s += c;
    }

    return Token(TokenType::ERROR_TOKEN, std::string("string didn't end with '\'\'\'' ?"));
  }

  while (current(&c)) {
    next();
    if (c == '\'') {
      return Token(TokenType::STRING, s);
    }

    s += c;
  }

  return Token(TokenType::ERROR_TOKEN, std::string("string didn't end with '\''?"));
}

inline Token Lexer::nextKey() {
  std::string s;
  char c;
  while (current(&c) && (isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '-')) {
    s += c;
    next();
  }

  if (s.empty()) return Token(TokenType::ERROR_TOKEN, std::string("Unknown key format"));

  return Token(TokenType::IDENT, s);
}

inline Token Lexer::nextValue() {
  std::string s;
  char c;

  if (current(&c) && isalpha(static_cast<unsigned char>(c))) {
    s += c;
    next();
    while (current(&c) && isalpha(static_cast<unsigned char>(c))) {
      s += c;
      next();
    }

    if (s == "true") return Token(TokenType::BOOL, true);
    if (s == "false") return Token(TokenType::BOOL, false);
    return Token(TokenType::ERROR_TOKEN, std::string("Unknown ident: ") + s);
  }

  while (current(&c) && (('0' <= c && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == 'T' ||
                         c == 'Z' || c == '_' || c == ':' || c == '-' || c == '+')) {
    next();
    s += c;
  }

  if (isInteger(s)) {
    std::stringstream ss(removeDelimiter(s));
    std::int64_t x;
    ss >> x;
    return Token(TokenType::INT, x);
  }

  if (isDouble(s)) {
    std::stringstream ss(removeDelimiter(s));
    double d;
    ss >> d;
    return Token(TokenType::DOUBLE, d);
  }

  return parseAsTime(s);
}

inline Token Lexer::parseAsTime(const std::string& str) {
  const char* s = str.c_str();

  int n;
  int YYYY, MM, DD;
#if defined(_MSC_VER)
  if (sscanf_s(s, "%d-%d-%d%n", &YYYY, &MM, &DD, &n) != 3)
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#else
  if (sscanf(s, "%d-%d-%d%n", &YYYY, &MM, &DD, &n) != 3)
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#endif

  if (!(1 <= MM && MM <= 12)) {
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
  }
  if (YYYY < 1900) {
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
  }

  if (s[n] == '\0') {
    std::tm t;
    t.tm_sec = 0;
    t.tm_min = 0;
    t.tm_hour = 0;
    t.tm_mday = DD;
    t.tm_mon = MM - 1;
    t.tm_year = YYYY - 1900;
    auto tp = std::chrono::system_clock::from_time_t(timegm(&t));
    return Token(TokenType::TIME, tp);
  }

  if (s[n] != 'T') return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));

  s = s + n + 1;

  int hh, mm;
  double ss;  // double for fraction
#if defined(_MSC_VER)
  if (sscanf_s(s, "%d:%d:%lf%n", &hh, &mm, &ss, &n) != 3)
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#else
  if (sscanf(s, "%d:%d:%lf%n", &hh, &mm, &ss, &n) != 3)
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#endif

  std::tm t;
  t.tm_sec = static_cast<int>(ss);
  t.tm_min = mm;
  t.tm_hour = hh;
  t.tm_mday = DD;
  t.tm_mon = MM - 1;
  t.tm_year = YYYY - 1900;
  auto tp = std::chrono::system_clock::from_time_t(timegm(&t));
  ss -= static_cast<int>(ss);
  // TODO(mayah): workaround GCC 4.9.3 on cygwin does not have std::round, but round().
  tp += std::chrono::microseconds(static_cast<std::int64_t>(round(ss * 1000000)));

  if (s[n] == '\0') return Token(TokenType::TIME, tp);

  if (s[n] == 'Z' && s[n + 1] == '\0') return Token(TokenType::TIME, tp);

  s = s + n;
  // offset
  // [+/-]%d:%d
  char pn;
  int oh, om;
#if defined(_MSC_VER)
  if (sscanf_s(s, "%c%d:%d", &pn, static_cast<unsigned>(sizeof(pn)), &oh, &om) != 3)
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#else
  if (sscanf(s, "%c%d:%d", &pn, &oh, &om) != 3)
    return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#endif

  if (pn != '+' && pn != '-') return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));

  if (pn == '+') {
    tp -= std::chrono::hours(oh);
    tp -= std::chrono::minutes(om);
  } else {
    tp += std::chrono::hours(oh);
    tp += std::chrono::minutes(om);
  }

  return Token(TokenType::TIME, tp);
}

inline Token Lexer::nextKeyToken() { return nextToken(false); }

inline Token Lexer::nextValueToken() { return nextToken(true); }

inline Token Lexer::nextToken(bool isValueToken) {
  char c;
  while (current(&c)) {
    if (c == ' ' || c == '\t' || c == '\r') {
      next();
      continue;
    }

    if (c == '#') {
      skipUntilNewLine();
      continue;
    }

    switch (c) {
      case '\n':
        next();
        return Token(TokenType::END_OF_LINE);
      case '=':
        next();
        return Token(TokenType::EQUAL);
      case '{':
        next();
        return Token(TokenType::LBRACE);
      case '}':
        next();
        return Token(TokenType::RBRACE);
      case '[':
        next();
        return Token(TokenType::LBRACKET);
      case ']':
        next();
        return Token(TokenType::RBRACKET);
      case ',':
        next();
        return Token(TokenType::COMMA);
      case '.':
        next();
        return Token(TokenType::DOT);
      case '\"':
        return nextStringDoubleQuote();
      case '\'':
        return nextStringSingleQuote();
      default:
        if (isValueToken) {
          return nextValue();
        } else {
          return nextKey();
        }
    }
  }

  return Token(TokenType::END_OF_FILE);
}

}  // namespace internal

// ----------------------------------------------------------------------

// static
inline const char* Value::typeToString(Value::Type type) {
  switch (type) {
    case NULL_TYPE:
      return "null";
    case BOOL_TYPE:
      return "bool";
    case INT_TYPE:
      return "int";
    case DOUBLE_TYPE:
      return "double";
    case STRING_TYPE:
      return "string";
    case TIME_TYPE:
      return "time";
    case ARRAY_TYPE:
      return "array";
    case TABLE_TYPE:
      return "table";
    default:
      return "unknown";
  }
}

inline Value::Value(const Value& v) : type_(v.type_) {
  switch (v.type_) {
    case NULL_TYPE:
      null_ = v.null_;
      break;
    case BOOL_TYPE:
      bool_ = v.bool_;
      break;
    case INT_TYPE:
      int_ = v.int_;
      break;
    case DOUBLE_TYPE:
      double_ = v.double_;
      break;
    case STRING_TYPE:
      string_ = new std::string(*v.string_);
      break;
    case TIME_TYPE:
      time_ = new Time(*v.time_);
      break;
    case ARRAY_TYPE:
      array_ = new Array(*v.array_);
      break;
    case TABLE_TYPE:
      table_ = new Table(*v.table_);
      break;
    default:
      assert(false);
      type_ = NULL_TYPE;
      null_ = nullptr;
  }
}

inline Value::Value(Value&& v) noexcept : type_(v.type_) {
  switch (v.type_) {
    case NULL_TYPE:
      null_ = v.null_;
      break;
    case BOOL_TYPE:
      bool_ = v.bool_;
      break;
    case INT_TYPE:
      int_ = v.int_;
      break;
    case DOUBLE_TYPE:
      double_ = v.double_;
      break;
    case STRING_TYPE:
      string_ = v.string_;
      break;
    case TIME_TYPE:
      time_ = v.time_;
      break;
    case ARRAY_TYPE:
      array_ = v.array_;
      break;
    case TABLE_TYPE:
      table_ = v.table_;
      break;
    default:
      assert(false);
      type_ = NULL_TYPE;
      null_ = nullptr;
  }

  v.type_ = NULL_TYPE;
  v.null_ = nullptr;
}

inline Value& Value::operator=(const Value& v) {
  if (this == &v) return *this;

  this->~Value();

  type_ = v.type_;
  switch (v.type_) {
    case NULL_TYPE:
      null_ = v.null_;
      break;
    case BOOL_TYPE:
      bool_ = v.bool_;
      break;
    case INT_TYPE:
      int_ = v.int_;
      break;
    case DOUBLE_TYPE:
      double_ = v.double_;
      break;
    case STRING_TYPE:
      string_ = new std::string(*v.string_);
      break;
    case TIME_TYPE:
      time_ = new Time(*v.time_);
      break;
    case ARRAY_TYPE:
      array_ = new Array(*v.array_);
      break;
    case TABLE_TYPE:
      table_ = new Table(*v.table_);
      break;
    default:
      assert(false);
      type_ = NULL_TYPE;
      null_ = nullptr;
  }

  return *this;
}

inline Value& Value::operator=(Value&& v) noexcept {
  if (this == &v) return *this;

  this->~Value();

  type_ = v.type_;
  switch (v.type_) {
    case NULL_TYPE:
      null_ = v.null_;
      break;
    case BOOL_TYPE:
      bool_ = v.bool_;
      break;
    case INT_TYPE:
      int_ = v.int_;
      break;
    case DOUBLE_TYPE:
      double_ = v.double_;
      break;
    case STRING_TYPE:
      string_ = v.string_;
      break;
    case TIME_TYPE:
      time_ = v.time_;
      break;
    case ARRAY_TYPE:
      array_ = v.array_;
      break;
    case TABLE_TYPE:
      table_ = v.table_;
      break;
    default:
      assert(false);
      type_ = NULL_TYPE;
      null_ = nullptr;
  }

  v.type_ = NULL_TYPE;
  v.null_ = nullptr;
  return *this;
}

inline Value::~Value() {
  switch (type_) {
    case STRING_TYPE:
      delete string_;
      break;
    case TIME_TYPE:
      delete time_;
      break;
    case ARRAY_TYPE:
      delete array_;
      break;
    case TABLE_TYPE:
      delete table_;
      break;
    default:
      break;
  }
}

inline size_t Value::size() const {
  switch (type_) {
    case NULL_TYPE:
      return 0;
    case ARRAY_TYPE:
      return array_->size();
    case TABLE_TYPE:
      return table_->size();
    default:
      return 1;
  }
}

inline bool Value::empty() const { return size() == 0; }

template <>
struct Value::ValueConverter<bool> {
  bool is(const Value& v) { return v.type() == Value::BOOL_TYPE; }
  bool to(const Value& v) {
    v.assureType<bool>();
    return v.bool_;
  }
};
template <>
struct Value::ValueConverter<int64_t> {
  bool is(const Value& v) { return v.type() == Value::INT_TYPE; }
  int64_t to(const Value& v) {
    v.assureType<int64_t>();
    return v.int_;
  }
};
template <>
struct Value::ValueConverter<int> {
  bool is(const Value& v) { return v.type() == Value::INT_TYPE; }
  int to(const Value& v) {
    v.assureType<int>();
    return static_cast<int>(v.int_);
  }
};
template <>
struct Value::ValueConverter<double> {
  bool is(const Value& v) { return v.type() == Value::DOUBLE_TYPE; }
  double to(const Value& v) {
    v.assureType<double>();
    return v.double_;
  }
};
template <>
struct Value::ValueConverter<std::string> {
  bool is(const Value& v) { return v.type() == Value::STRING_TYPE; }
  const std::string& to(const Value& v) {
    v.assureType<std::string>();
    return *v.string_;
  }
};
template <>
struct Value::ValueConverter<Time> {
  bool is(const Value& v) { return v.type() == Value::TIME_TYPE; }
  const Time& to(const Value& v) {
    v.assureType<Time>();
    return *v.time_;
  }
};
template <>
struct Value::ValueConverter<Array> {
  bool is(const Value& v) { return v.type() == Value::ARRAY_TYPE; }
  const Array& to(const Value& v) {
    v.assureType<Array>();
    return *v.array_;
  }
};
template <>
struct Value::ValueConverter<Table> {
  bool is(const Value& v) { return v.type() == Value::TABLE_TYPE; }
  const Table& to(const Value& v) {
    v.assureType<Table>();
    return *v.table_;
  }
};

template <typename T>
struct Value::ValueConverter<std::vector<T>> {
  bool is(const Value& v) {
    if (v.type() != Value::ARRAY_TYPE) return false;
    const Array& array = v.as<Array>();
    if (array.empty()) return true;
    return array.front().is<T>();
  }

  std::vector<T> to(const Value& v) {
    const Array& array = v.as<Array>();
    if (array.empty()) return std::vector<T>();
    array.front().assureType<T>();

    std::vector<T> result;
    for (const auto& element : array) {
      result.push_back(element.as<T>());
    }

    return result;
  }
};

namespace internal {
template <typename T>
inline const char* type_name();
template <>
inline const char* type_name<bool>() {
  return "bool";
}
template <>
inline const char* type_name<int>() {
  return "int";
}
template <>
inline const char* type_name<int64_t>() {
  return "int64_t";
}
template <>
inline const char* type_name<double>() {
  return "double";
}
template <>
inline const char* type_name<std::string>() {
  return "string";
}
template <>
inline const char* type_name<toml::Time>() {
  return "time";
}
template <>
inline const char* type_name<toml::Array>() {
  return "array";
}
template <>
inline const char* type_name<toml::Table>() {
  return "table";
}
}  // namespace internal

template <typename T>
inline void Value::assureType() const {
  if (!is<T>())
    failwith("type error: this value is ", typeToString(type_), " but ", internal::type_name<T>(),
             " was requested");
}

template <typename T>
inline bool Value::is() const {
  return ValueConverter<T>().is(*this);
}

template <typename T>
inline typename call_traits<T>::return_type Value::as() const {
  return ValueConverter<T>().to(*this);
}

inline bool Value::isNumber() const { return is<int>() || is<double>(); }

inline double Value::asNumber() const {
  if (is<int>()) return as<int>();
  if (is<double>()) return as<double>();

  failwith("type error: this value is ", typeToString(type_), " but number is requested");
}

inline std::time_t Value::as_time_t() const {
  return std::chrono::system_clock::to_time_t(as<Time>());
}

inline std::string Value::spaces(int num) {
  if (num <= 0) return std::string();

  return std::string(num, ' ');
}

inline std::string Value::escapeKey(const std::string& key) {
  auto position = std::find_if(key.begin(), key.end(), [](char c) -> bool {
    if (std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '-') return false;
    return true;
  });

  if (position != key.end()) {
    std::string escaped = "\"";
    for (const char& c : key) {
      if (c == '\\' || c == '"') escaped += '\\';
      escaped += c;
    }
    escaped += "\"";

    return escaped;
  }

  return key;
}

inline void Value::write(std::ostream* os, const std::string& keyPrefix, int indent) const {
  switch (type_) {
    case NULL_TYPE:
      failwith("null type value is not a valid value");
      break;
    case BOOL_TYPE:
      (*os) << (bool_ ? "true" : "false");
      break;
    case INT_TYPE:
      (*os) << int_;
      break;
    case DOUBLE_TYPE: {
      (*os) << std::fixed << std::showpoint << double_;
      break;
    }
    case STRING_TYPE:
      (*os) << '"' << internal::escapeString(*string_) << '"';
      break;
    case TIME_TYPE: {
      time_t tt = std::chrono::system_clock::to_time_t(*time_);
      std::tm t;
      gmtime_r(&tt, &t);
      char buf[256];
      snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02dZ", t.tm_year + 1900, t.tm_mon + 1,
               t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
      (*os) << buf;
      break;
    }
    case ARRAY_TYPE:
      (*os) << '[';
      for (size_t i = 0; i < array_->size(); ++i) {
        if (i) (*os) << ", ";
        (*array_)[i].write(os, keyPrefix, -1);
      }
      (*os) << ']';
      break;
    case TABLE_TYPE:
      for (const auto& kv : *table_) {
        if (kv.second.is<Table>()) continue;
        if (kv.second.is<Array>() && kv.second.size() > 0 && kv.second.find(0)->is<Table>())
          continue;
        (*os) << spaces(indent) << escapeKey(kv.first) << " = ";
        kv.second.write(os, keyPrefix, indent >= 0 ? indent + 1 : indent);
        (*os) << '\n';
      }
      for (const auto& kv : *table_) {
        if (kv.second.is<Table>()) {
          std::string key(keyPrefix);
          if (!keyPrefix.empty()) key += ".";
          key += escapeKey(kv.first);
          (*os) << "\n" << spaces(indent) << "[" << key << "]\n";
          kv.second.write(os, key, indent >= 0 ? indent + 1 : indent);
        }
        if (kv.second.is<Array>() && kv.second.size() > 0 && kv.second.find(0)->is<Table>()) {
          std::string key(keyPrefix);
          if (!keyPrefix.empty()) key += ".";
          key += escapeKey(kv.first);
          for (const auto& v : kv.second.as<Array>()) {
            (*os) << "\n" << spaces(indent) << "[[" << key << "]]\n";
            v.write(os, key, indent >= 0 ? indent + 1 : indent);
          }
        }
      }
      break;
    default:
      failwith("writing unknown type");
      break;
  }
}

inline void Value::writeFormatted(std::ostream* os, FormatFlag flags) const {
  int indent = flags & FORMAT_INDENT ? 0 : -1;

  write(os, std::string(), indent);
}

// static
inline FormatFlag operator|(FormatFlag lhs, FormatFlag rhs) {
  return static_cast<FormatFlag>(static_cast<int>(lhs) | static_cast<int>(rhs));
}

// static
inline std::ostream& operator<<(std::ostream& os, const toml::Value& v) {
  v.write(&os);
  return os;
}

// static
inline bool operator==(const Value& lhs, const Value& rhs) {
  if (lhs.type() != rhs.type()) return false;

  switch (lhs.type()) {
    case Value::Type::NULL_TYPE:
      return true;
    case Value::Type::BOOL_TYPE:
      return lhs.bool_ == rhs.bool_;
    case Value::Type::INT_TYPE:
      return lhs.int_ == rhs.int_;
    case Value::Type::DOUBLE_TYPE:
      return lhs.double_ == rhs.double_;
    case Value::Type::STRING_TYPE:
      return *lhs.string_ == *rhs.string_;
    case Value::Type::TIME_TYPE:
      return *lhs.time_ == *rhs.time_;
    case Value::Type::ARRAY_TYPE:
      return *lhs.array_ == *rhs.array_;
    case Value::Type::TABLE_TYPE:
      return *lhs.table_ == *rhs.table_;
    default:
      failwith("unknown type");
  }
}

template <typename T>
inline typename call_traits<T>::return_type Value::get(const std::string& key) const {
  if (!is<Table>()) failwith("type must be table to do get(key).");

  const Value* obj = find(key);
  if (!obj) failwith("key ", key, " was not found.");

  return obj->as<T>();
}

inline const Value* Value::find(const std::string& key) const {
  if (!is<Table>()) return nullptr;

  std::istringstream ss(key);
  internal::Lexer lexer(ss);

  const Value* current = this;
  while (true) {
    internal::Token t = lexer.nextKeyToken();
    if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING))
      return nullptr;

    std::string part = t.strValue();
    t = lexer.nextKeyToken();
    if (t.type() == internal::TokenType::DOT) {
      current = current->findChild(part);
      if (!current || !current->is<Table>()) return nullptr;
    } else if (t.type() == internal::TokenType::END_OF_FILE) {
      return current->findChild(part);
    } else {
      return nullptr;
    }
  }
}

inline Value* Value::find(const std::string& key) {
  return const_cast<Value*>(const_cast<const Value*>(this)->find(key));
}

inline bool Value::merge(const toml::Value& v) {
  if (this == &v) return true;
  if (!is<Table>() || !v.is<Table>()) return false;

  for (const auto& kv : *v.table_) {
    if (Value* tmp = find(kv.first)) {
      // If both are table, we merge them.
      if (tmp->is<Table>() && kv.second.is<Table>()) {
        if (!tmp->merge(kv.second)) return false;
      } else {
        setChild(kv.first, kv.second);
      }
    } else {
      setChild(kv.first, kv.second);
    }
  }

  return true;
}

inline Value* Value::set(const std::string& key, const Value& v) {
  Value* result = ensureValue(key);
  *result = v;
  return result;
}

inline Value* Value::setChild(const std::string& key, const Value& v) {
  if (!valid()) *this = Value((Table()));

  if (!is<Table>()) failwith("type must be table to do set(key, v).");

  (*table_)[key] = v;
  return &(*table_)[key];
}

inline Value* Value::setChild(const std::string& key, Value&& v) {
  if (!valid()) *this = Value((Table()));

  if (!is<Table>()) failwith("type must be table to do set(key, v).");

  (*table_)[key] = std::move(v);
  return &(*table_)[key];
}

inline bool Value::erase(const std::string& key) {
  if (!is<Table>()) return false;

  std::istringstream ss(key);
  internal::Lexer lexer(ss);

  Value* current = this;
  while (true) {
    internal::Token t = lexer.nextKeyToken();
    if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING))
      return false;

    std::string part = t.strValue();
    t = lexer.nextKeyToken();
    if (t.type() == internal::TokenType::DOT) {
      current = current->findChild(part);
      if (!current || !current->is<Table>()) return false;
    } else if (t.type() == internal::TokenType::END_OF_FILE) {
      return current->eraseChild(part);
    } else {
      return false;
    }
  }
}

inline bool Value::eraseChild(const std::string& key) {
  if (!is<Table>()) failwith("type must be table to do erase(key).");

  return table_->erase(key) > 0;
}

inline Value& Value::operator[](const std::string& key) {
  if (!valid()) *this = Value((Table()));

  if (Value* v = findChild(key)) return *v;

  return *setChild(key, Value());
}

template <typename T>
inline typename call_traits<T>::return_type Value::get(size_t index) const {
  if (!is<Array>()) failwith("type must be array to do get(index).");

  if (array_->size() <= index) failwith("index out of bound");

  return (*array_)[index].as<T>();
}

inline const Value* Value::find(size_t index) const {
  if (!is<Array>()) return nullptr;
  if (index < array_->size()) return &(*array_)[index];
  return nullptr;
}

inline Value* Value::find(size_t index) {
  return const_cast<Value*>(const_cast<const Value*>(this)->find(index));
}

inline Value* Value::push(const Value& v) {
  if (!valid())
    *this = Value((Array()));
  else if (!is<Array>())
    failwith("type must be array to do push(Value).");

  array_->push_back(v);
  return &array_->back();
}

inline Value* Value::push(Value&& v) {
  if (!valid())
    *this = Value((Array()));
  else if (!is<Array>())
    failwith("type must be array to do push(Value).");

  array_->push_back(std::move(v));
  return &array_->back();
}

inline Value* Value::ensureValue(const std::string& key) {
  if (!valid()) *this = Value((Table()));
  if (!is<Table>()) {
    failwith("encountered non table value");
  }

  std::istringstream ss(key);
  internal::Lexer lexer(ss);

  Value* current = this;
  while (true) {
    internal::Token t = lexer.nextKeyToken();
    if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING)) {
      failwith("invalid key");
    }

    std::string part = t.strValue();
    t = lexer.nextKeyToken();
    if (t.type() == internal::TokenType::DOT) {
      if (Value* candidate = current->findChild(part)) {
        if (!candidate->is<Table>()) failwith("encountered non table value");

        current = candidate;
      } else {
        current = current->setChild(part, Table());
      }
    } else if (t.type() == internal::TokenType::END_OF_FILE) {
      if (Value* v = current->findChild(part)) return v;
      return current->setChild(part, Value());
    } else {
      failwith("invalid key");
    }
  }
}

inline Value* Value::findChild(const std::string& key) {
  assert(is<Table>());

  auto it = table_->find(key);
  if (it == table_->end()) return nullptr;

  return &it->second;
}

inline const Value* Value::findChild(const std::string& key) const {
  assert(is<Table>());

  auto it = table_->find(key);
  if (it == table_->end()) return nullptr;

  return &it->second;
}

// ----------------------------------------------------------------------

namespace internal {

inline void Parser::skipForKey() {
  while (token().type() == TokenType::END_OF_LINE) nextKey();
}

inline void Parser::skipForValue() {
  while (token().type() == TokenType::END_OF_LINE) nextValue();
}

inline bool Parser::consumeForKey(TokenType type) {
  if (token().type() == type) {
    nextKey();
    return true;
  }

  return false;
}

inline bool Parser::consumeForValue(TokenType type) {
  if (token().type() == type) {
    nextValue();
    return true;
  }

  return false;
}

inline bool Parser::consumeEOLorEOFForKey() {
  if (token().type() == TokenType::END_OF_LINE || token().type() == TokenType::END_OF_FILE) {
    nextKey();
    return true;
  }

  return false;
}

inline void Parser::addError(const std::string& reason) {
  if (!errorReason_.empty()) return;

  std::stringstream ss;
  ss << "Error: line " << lexer_.lineNo() << ": " << reason;
  errorReason_ = ss.str();
}

inline const std::string& Parser::errorReason() { return errorReason_; }

inline Value Parser::parse() {
  Value root((Table()));
  Value* currentValue = &root;

  while (true) {
    skipForKey();
    if (token().type() == TokenType::END_OF_FILE) break;
    if (token().type() == TokenType::LBRACKET) {
      currentValue = parseGroupKey(&root);
      if (!currentValue) {
        addError("error when parsing group key");
        return Value();
      }
      continue;
    }

    if (!parseKeyValue(currentValue)) {
      addError("error when parsing key Value");
      return Value();
    }
  }
  return root;
}

inline Value* Parser::parseGroupKey(Value* root) {
  if (!consumeForKey(TokenType::LBRACKET)) return nullptr;

  bool isArray = false;
  if (token().type() == TokenType::LBRACKET) {
    nextKey();
    isArray = true;
  }

  Value* currentValue = root;
  while (true) {
    if (token().type() != TokenType::IDENT && token().type() != TokenType::STRING) return nullptr;

    std::string key = token().strValue();
    nextKey();

    if (token().type() == TokenType::DOT) {
      nextKey();
      if (Value* candidate = currentValue->findChild(key)) {
        if (candidate->is<Array>() && candidate->size() > 0)
          candidate = candidate->find(candidate->size() - 1);
        if (!candidate->is<Table>()) return nullptr;
        currentValue = candidate;
      } else {
        currentValue = currentValue->setChild(key, Table());
      }
      continue;
    }

    if (token().type() == TokenType::RBRACKET) {
      nextKey();
      if (Value* candidate = currentValue->findChild(key)) {
        if (isArray) {
          if (!candidate->is<Array>()) return nullptr;
          currentValue = candidate->push(Table());
        } else {
          if (candidate->is<Array>() && candidate->size() > 0)
            candidate = candidate->find(candidate->size() - 1);
          if (!candidate->is<Table>()) return nullptr;
          currentValue = candidate;
        }
      } else {
        if (isArray) {
          currentValue = currentValue->setChild(key, Array());
          currentValue = currentValue->push(Table());
        } else {
          currentValue = currentValue->setChild(key, Table());
        }
      }
      break;
    }

    return nullptr;
  }

  if (isArray) {
    if (!consumeForKey(TokenType::RBRACKET)) return nullptr;
  }

  if (!consumeEOLorEOFForKey()) return nullptr;

  return currentValue;
}

inline bool Parser::parseKeyValue(Value* current) {
  std::string key;
  if (!parseKey(&key)) {
    addError("parse key failed");
    return false;
  }
  if (!consumeForValue(TokenType::EQUAL)) {
    addError("no equal?");
    return false;
  }

  Value v;
  if (!parseValue(&v)) return false;
  if (!consumeEOLorEOFForKey()) return false;

  if (current->has(key)) {
    addError("Multiple same key: " + key);
    return false;
  }

  current->setChild(key, std::move(v));
  return true;
}

inline bool Parser::parseKey(std::string* key) {
  key->clear();

  if (token().type() == TokenType::IDENT || token().type() == TokenType::STRING) {
    *key = token().strValue();
    nextValue();
    return true;
  }

  return false;
}

inline bool Parser::parseValue(Value* v) {
  switch (token().type()) {
    case TokenType::STRING:
    case TokenType::MULTILINE_STRING:
      *v = token().strValue();
      nextValue();
      return true;
    case TokenType::LBRACKET:
      return parseArray(v);
    case TokenType::LBRACE:
      return parseInlineTable(v);
    case TokenType::BOOL:
      *v = token().boolValue();
      nextValue();
      return true;
    case TokenType::INT:
      *v = token().intValue();
      nextValue();
      return true;
    case TokenType::DOUBLE:
      *v = token().doubleValue();
      nextValue();
      return true;
    case TokenType::TIME:
      *v = token().timeValue();
      nextValue();
      return true;
    case TokenType::ERROR_TOKEN:
      addError(token().strValue());
      return false;
    default:
      addError("unexpected token");
      return false;
  }
}

inline bool Parser::parseBool(Value* v) {
  if (token().strValue() == "true") {
    nextValue();
    *v = true;
    return true;
  }

  if (token().strValue() == "false") {
    nextValue();
    *v = false;
    return true;
  }

  return false;
}

inline bool Parser::parseArray(Value* v) {
  if (!consumeForValue(TokenType::LBRACKET)) return false;

  Array a;
  while (true) {
    skipForValue();

    if (token().type() == TokenType::RBRACKET) break;

    skipForValue();
    Value x;
    if (!parseValue(&x)) return false;

    if (!a.empty()) {
      if (a.front().type() != x.type()) {
        addError("type check failed");
        return false;
      }
    }

    a.push_back(std::move(x));
    skipForValue();
    if (token().type() == TokenType::RBRACKET) break;
    if (token().type() == TokenType::COMMA) nextValue();
  }

  if (!consumeForValue(TokenType::RBRACKET)) return false;
  *v = std::move(a);
  return true;
}

inline bool Parser::parseInlineTable(Value* value) {
  // For inline table, next is KEY, so use consumeForKey here.
  if (!consumeForKey(TokenType::LBRACE)) return false;

  Value t((Table()));
  bool first = true;
  while (true) {
    if (token().type() == TokenType::RBRACE) {
      break;
    }

    if (!first) {
      if (token().type() != TokenType::COMMA) {
        addError("inline table didn't have ',' for delimiter?");
        return false;
      }
      nextKey();
    }
    first = false;

    std::string key;
    if (!parseKey(&key)) return false;
    if (!consumeForValue(TokenType::EQUAL)) return false;
    Value v;
    if (!parseValue(&v)) return false;

    if (t.has(key)) {
      addError("inline table has multiple same keys: key=" + key);
      return false;
    }

    t.set(key, v);
  }

  if (!consumeForValue(TokenType::RBRACE)) return false;
  *value = std::move(t);
  return true;
}

}  // namespace internal
}  // namespace toml

#endif  // TINYTOML_H_