// Copyright (c) 2011-2016 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "JsonValue.h"
#include <iomanip>
#include <sstream>

namespace Common {

JsonValue::JsonValue() : type(NIL) {
}

JsonValue::JsonValue(const JsonValue& other) {
  switch (other.type) {
  case ARRAY:
    new(valueArray)Array(*reinterpret_cast<const Array*>(other.valueArray));
    break;
  case BOOL:
    valueBool = other.valueBool;
    break;
  case INTEGER:
    valueInteger = other.valueInteger;
    break;
  case NIL:
    break;
  case OBJECT:
    new(valueObject)Object(*reinterpret_cast<const Object*>(other.valueObject));
    break;
  case REAL:
    valueReal = other.valueReal;
    break;
  case STRING:
    new(valueString)String(*reinterpret_cast<const String*>(other.valueString));
    break;
  }

  type = other.type;
}

JsonValue::JsonValue(JsonValue&& other) {
  switch (other.type) {
  case ARRAY:
    new(valueArray)Array(std::move(*reinterpret_cast<Array*>(other.valueArray)));
    reinterpret_cast<Array*>(other.valueArray)->~Array();
    break;
  case BOOL:
    valueBool = other.valueBool;
    break;
  case INTEGER:
    valueInteger = other.valueInteger;
    break;
  case NIL:
    break;
  case OBJECT:
    new(valueObject)Object(std::move(*reinterpret_cast<Object*>(other.valueObject)));
    reinterpret_cast<Object*>(other.valueObject)->~Object();
    break;
  case REAL:
    valueReal = other.valueReal;
    break;
  case STRING:
    new(valueString)String(std::move(*reinterpret_cast<String*>(other.valueString)));
    reinterpret_cast<String*>(other.valueString)->~String();
    break;
  }

  type = other.type;
  other.type = NIL;
}

JsonValue::JsonValue(Type valueType) {
  switch (valueType) {
  case ARRAY:
    new(valueArray)Array;
    break;
  case NIL:
    break;
  case OBJECT:
    new(valueObject)Object;
    break;
  case STRING:
    new(valueString)String;
    break;
  default:
    throw std::runtime_error("Invalid JsonValue type for constructor");
  }

  type = valueType;
}

JsonValue::JsonValue(const Array& value) {
  new(valueArray)Array(value);
  type = ARRAY;
}

JsonValue::JsonValue(Array&& value) {
  new(valueArray)Array(std::move(value));
  type = ARRAY;
}

JsonValue::JsonValue(Bool value) : type(BOOL), valueBool(value) {
}

JsonValue::JsonValue(Integer value) : type(INTEGER), valueInteger(value) {
}

JsonValue::JsonValue(Nil) : type(NIL) {
}

JsonValue::JsonValue(const Object& value) {
  new(valueObject)Object(value);
  type = OBJECT;
}

JsonValue::JsonValue(Object&& value) {
  new(valueObject)Object(std::move(value));
  type = OBJECT;
}

JsonValue::JsonValue(Real value) : type(REAL), valueReal(value) {
}

JsonValue::JsonValue(const String& value) {
  new(valueString)String(value);
  type = STRING;
}

JsonValue::JsonValue(String&& value) {
  new(valueString)String(std::move(value));
  type = STRING;
}

JsonValue::~JsonValue() {
  destructValue();
}

JsonValue& JsonValue::operator=(const JsonValue& other) {
  if (type != other.type) {
    destructValue();
    switch (other.type) {
    case ARRAY:
      type = NIL;
      new(valueArray)Array(*reinterpret_cast<const Array*>(other.valueArray));
      break;
    case BOOL:
      valueBool = other.valueBool;
      break;
    case INTEGER:
      valueInteger = other.valueInteger;
      break;
    case NIL:
      break;
    case OBJECT:
      type = NIL;
      new(valueObject)Object(*reinterpret_cast<const Object*>(other.valueObject));
      break;
    case REAL:
      valueReal = other.valueReal;
      break;
    case STRING:
      type = NIL;
      new(valueString)String(*reinterpret_cast<const String*>(other.valueString));
      break;
    }

    type = other.type;
  } else {
    switch (type) {
    case ARRAY:
      *reinterpret_cast<Array*>(valueArray) = *reinterpret_cast<const Array*>(other.valueArray);
      break;
    case BOOL:
      valueBool = other.valueBool;
      break;
    case INTEGER:
      valueInteger = other.valueInteger;
      break;
    case NIL:
      break;
    case OBJECT:
      *reinterpret_cast<Object*>(valueObject) = *reinterpret_cast<const Object*>(other.valueObject);
      break;
    case REAL:
      valueReal = other.valueReal;
      break;
    case STRING:
      *reinterpret_cast<String*>(valueString) = *reinterpret_cast<const String*>(other.valueString);
      break;
    }
  }

  return *this;
}

JsonValue& JsonValue::operator=(JsonValue&& other) {
  if (type != other.type) {
    destructValue();
    switch (other.type) {
    case ARRAY:
      type = NIL;
      new(valueArray)Array(std::move(*reinterpret_cast<const Array*>(other.valueArray)));
      reinterpret_cast<Array*>(other.valueArray)->~Array();
      break;
    case BOOL:
      valueBool = other.valueBool;
      break;
    case INTEGER:
      valueInteger = other.valueInteger;
      break;
    case NIL:
      break;
    case OBJECT:
      type = NIL;
      new(valueObject)Object(std::move(*reinterpret_cast<const Object*>(other.valueObject)));
      reinterpret_cast<Object*>(other.valueObject)->~Object();
      break;
    case REAL:
      valueReal = other.valueReal;
      break;
    case STRING:
      type = NIL;
      new(valueString)String(std::move(*reinterpret_cast<const String*>(other.valueString)));
      reinterpret_cast<String*>(other.valueString)->~String();
      break;
    }

    type = other.type;
  } else {
    switch (type) {
    case ARRAY:
      *reinterpret_cast<Array*>(valueArray) = std::move(*reinterpret_cast<const Array*>(other.valueArray));
      reinterpret_cast<Array*>(other.valueArray)->~Array();
      break;
    case BOOL:
      valueBool = other.valueBool;
      break;
    case INTEGER:
      valueInteger = other.valueInteger;
      break;
    case NIL:
      break;
    case OBJECT:
      *reinterpret_cast<Object*>(valueObject) = std::move(*reinterpret_cast<const Object*>(other.valueObject));
      reinterpret_cast<Object*>(other.valueObject)->~Object();
      break;
    case REAL:
      valueReal = other.valueReal;
      break;
    case STRING:
      *reinterpret_cast<String*>(valueString) = std::move(*reinterpret_cast<const String*>(other.valueString));
      reinterpret_cast<String*>(other.valueString)->~String();
      break;
    }
  }

  other.type = NIL;
  return *this;
}

JsonValue& JsonValue::operator=(const Array& value) {
  if (type != ARRAY) {
    destructValue();
    type = NIL;
    new(valueArray)Array(value);
    type = ARRAY;
  } else {
    *reinterpret_cast<Array*>(valueArray) = value;
  }

  return *this;
}

JsonValue& JsonValue::operator=(Array&& value) {
  if (type != ARRAY) {
    destructValue();
    type = NIL;
    new(valueArray)Array(std::move(value));
    type = ARRAY;
  } else {
    *reinterpret_cast<Array*>(valueArray) = std::move(value);
  }

  return *this;
}

//JsonValue& JsonValue::operator=(Bool value) {
//  if (type != BOOL) {
//    destructValue();
//    type = BOOL;
//  }
//
//  valueBool = value;
//  return *this;
//}

JsonValue& JsonValue::operator=(Integer value) {
  if (type != INTEGER) {
    destructValue();
    type = INTEGER;
  }

  valueInteger = value;
  return *this;
}

JsonValue& JsonValue::operator=(Nil) {
  if (type != NIL) {
    destructValue();
    type = NIL;
  }

  return *this;
}

JsonValue& JsonValue::operator=(const Object& value) {
  if (type != OBJECT) {
    destructValue();
    type = NIL;
    new(valueObject)Object(value);
    type = OBJECT;
  } else {
    *reinterpret_cast<Object*>(valueObject) = value;
  }

  return *this;
}

JsonValue& JsonValue::operator=(Object&& value) {
  if (type != OBJECT) {
    destructValue();
    type = NIL;
    new(valueObject)Object(std::move(value));
    type = OBJECT;
  } else {
    *reinterpret_cast<Object*>(valueObject) = std::move(value);
  }

  return *this;
}

JsonValue& JsonValue::operator=(Real value) {
  if (type != REAL) {
    destructValue();
    type = REAL;
  }

  valueReal = value;
  return *this;
}

JsonValue& JsonValue::operator=(const String& value) {
  if (type != STRING) {
    destructValue();
    type = NIL;
    new(valueString)String(value);
    type = STRING;
  } else {
    *reinterpret_cast<String*>(valueString) = value;
  }

  return *this;
}

JsonValue& JsonValue::operator=(String&& value) {
  if (type != STRING) {
    destructValue();
    type = NIL;
    new(valueString)String(std::move(value));
    type = STRING;
  } else {
    *reinterpret_cast<String*>(valueString) = std::move(value);
  }

  return *this;
}

bool JsonValue::isArray() const {
  return type == ARRAY;
}

bool JsonValue::isBool() const {
  return type == BOOL;
}

bool JsonValue::isInteger() const {
  return type == INTEGER;
}

bool JsonValue::isNil() const {
  return type == NIL;
}

bool JsonValue::isObject() const {
  return type == OBJECT;
}

bool JsonValue::isReal() const {
  return type == REAL;
}

bool JsonValue::isString() const {
  return type == STRING;
}

JsonValue::Type JsonValue::getType() const {
  return type;
}

JsonValue::Array& JsonValue::getArray() {
  if (type != ARRAY) {
    throw std::runtime_error("JsonValue type is not ARRAY");
  }

  return *reinterpret_cast<Array*>(valueArray);
}

const JsonValue::Array& JsonValue::getArray() const {
  if (type != ARRAY) {
    throw std::runtime_error("JsonValue type is not ARRAY");
  }

  return *reinterpret_cast<const Array*>(valueArray);
}

JsonValue::Bool JsonValue::getBool() const {
  if (type != BOOL) {
    throw std::runtime_error("JsonValue type is not BOOL");
  }

  return valueBool;
}

JsonValue::Integer JsonValue::getInteger() const {
  if (type != INTEGER) {
    throw std::runtime_error("JsonValue type is not INTEGER");
  }

  return valueInteger;
}

JsonValue::Object& JsonValue::getObject() {
  if (type != OBJECT) {
    throw std::runtime_error("JsonValue type is not OBJECT");
  }

  return *reinterpret_cast<Object*>(valueObject);
}

const JsonValue::Object& JsonValue::getObject() const {
  if (type != OBJECT) {
    throw std::runtime_error("JsonValue type is not OBJECT");
  }

  return *reinterpret_cast<const Object*>(valueObject);
}

JsonValue::Real JsonValue::getReal() const {
  if (type != REAL) {
    throw std::runtime_error("JsonValue type is not REAL");
  }

  return valueReal;
}

JsonValue::String& JsonValue::getString() {
  if (type != STRING) {
    throw std::runtime_error("JsonValue type is not STRING");
  }

  return *reinterpret_cast<String*>(valueString);
}

const JsonValue::String& JsonValue::getString() const {
  if (type != STRING) {
    throw std::runtime_error("JsonValue type is not STRING");
  }

  return *reinterpret_cast<const String*>(valueString);
}

size_t JsonValue::size() const {
  switch (type) {
  case ARRAY:
    return reinterpret_cast<const Array*>(valueArray)->size();
  case OBJECT:
    return reinterpret_cast<const Object*>(valueObject)->size();
  default:
    throw std::runtime_error("JsonValue type is not ARRAY or OBJECT");
  }
}

JsonValue& JsonValue::operator[](size_t index) {
  if (type != ARRAY) {
    throw std::runtime_error("JsonValue type is not ARRAY");
  }

  return reinterpret_cast<Array*>(valueArray)->at(index);
}

const JsonValue& JsonValue::operator[](size_t index) const {
  if (type != ARRAY) {
    throw std::runtime_error("JsonValue type is not ARRAY");
  }

  return reinterpret_cast<const Array*>(valueArray)->at(index);
}

JsonValue& JsonValue::pushBack(const JsonValue& value) {
  if (type != ARRAY) {
    throw std::runtime_error("JsonValue type is not ARRAY");
  }

  reinterpret_cast<Array*>(valueArray)->emplace_back(value);
  return reinterpret_cast<Array*>(valueArray)->back();
}

JsonValue& JsonValue::pushBack(JsonValue&& value) {
  if (type != ARRAY) {
    throw std::runtime_error("JsonValue type is not ARRAY");
  }

  reinterpret_cast<Array*>(valueArray)->emplace_back(std::move(value));
  return reinterpret_cast<Array*>(valueArray)->back();
}

JsonValue& JsonValue::operator()(const Key& key) {
  return getObject().at(key);
}

const JsonValue& JsonValue::operator()(const Key& key) const {
  return getObject().at(key);
}

bool JsonValue::contains(const Key& key) const {
  return getObject().count(key) > 0;
}

JsonValue& JsonValue::insert(const Key& key, const JsonValue& value) {
  return getObject().emplace(key, value).first->second;
}

JsonValue& JsonValue::insert(const Key& key, JsonValue&& value) {
  return getObject().emplace(key, std::move(value)).first->second;
}

JsonValue& JsonValue::set(const Key& key, const JsonValue& value) {
  getObject()[key] = value;
  return *this;
}

JsonValue& JsonValue::set(const Key& key, JsonValue&& value) {
  getObject()[key] = std::move(value);
  return *this;
}

size_t JsonValue::erase(const Key& key) {
  return getObject().erase(key);
}

JsonValue JsonValue::fromString(const std::string& source) {
  JsonValue jsonValue;
  std::istringstream stream(source);
  stream >> jsonValue;
  if (stream.fail()) {
    throw std::runtime_error("Unable to parse JsonValue");
  }

  return jsonValue;
}

std::string JsonValue::toString() const {
  std::ostringstream stream;
  stream << *this;
  return stream.str();
}

std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) {
  switch (jsonValue.type) {
  case JsonValue::ARRAY: {
    const JsonValue::Array& array = *reinterpret_cast<const JsonValue::Array*>(jsonValue.valueArray);
    out << '[';
    if (array.size() > 0) {
      out << array[0];
      for (size_t i = 1; i < array.size(); ++i) {
        out << ',' << array[i];
      }
    }

    out << ']';
    break;
  }
  case JsonValue::BOOL:
    out << (jsonValue.valueBool ? "true" : "false");
    break;
  case JsonValue::INTEGER:
    out << jsonValue.valueInteger;
    break;
  case JsonValue::NIL:
    out << "null";
    break;
  case JsonValue::OBJECT: {
    const JsonValue::Object& object = *reinterpret_cast<const JsonValue::Object*>(jsonValue.valueObject);
    out << '{';
    auto iter = object.begin();
    if (iter != object.end()) {
      out << '"' << iter->first << "\":" << iter->second;
      ++iter;
      for (; iter != object.end(); ++iter) {
        out << ",\"" << iter->first << "\":" << iter->second;
      }
    }

    out << '}';
    break;
  }
  case JsonValue::REAL: {
    std::ostringstream stream;
    stream << std::fixed << std::setprecision(11) << jsonValue.valueReal;
    std::string value = stream.str();
    while (value.size() > 1 && value[value.size() - 2] != '.' && value[value.size() - 1] == '0') {
      value.resize(value.size() - 1);
    }

    out << value;
    break;
  }
  case JsonValue::STRING:
    out << '"' << *reinterpret_cast<const JsonValue::String*>(jsonValue.valueString) << '"';
    break;
  }

  return out;
}


namespace {

char readChar(std::istream& in) {
  char c;

  if (!(in >> c)) {
    throw std::runtime_error("Unable to parse: unexpected end of stream");
  }

  return c;
}

char readNonWsChar(std::istream& in) {
  char c;

  do {
    c = readChar(in);
  } while (isspace(c));

  return c;
}

std::string readStringToken(std::istream& in) {
  char c;
  std::string value;

  while (in) {
    c = readChar(in);

    if (c == '"') {
      break;
    }

    if (c == '\\') {
      value += c;
      c = readChar(in);
    }

    value += c;
  }

  return value;
}

}


std::istream& operator>>(std::istream& in, JsonValue& jsonValue) {
  char c = readNonWsChar(in);

  if (c == '[') {
    jsonValue.readArray(in);
  } else if (c == 't') {
    jsonValue.readTrue(in);
  } else if (c == 'f') {
    jsonValue.readFalse(in);
  } else if ((c == '-') || (c >= '0' && c <= '9')) {
    jsonValue.readNumber(in, c);
  } else if (c == 'n') {
    jsonValue.readNull(in);
  } else if (c == '{') {
    jsonValue.readObject(in);
  } else if (c == '"') {
    jsonValue.readString(in);
  } else {
    throw std::runtime_error("Unable to parse");
  }

  return in;
}

void JsonValue::destructValue() {
  switch (type) {
  case ARRAY:
    reinterpret_cast<Array*>(valueArray)->~Array();
    break;
  case OBJECT:
    reinterpret_cast<Object*>(valueObject)->~Object();
    break;
  case STRING:
    reinterpret_cast<String*>(valueString)->~String();
    break;
  default:
    break;
  }
}

void JsonValue::readArray(std::istream& in) {
  JsonValue::Array value;
  char c = readNonWsChar(in);

  if(c != ']') {
    in.putback(c);
    for (;;) {
      value.resize(value.size() + 1);
      in >> value.back();
      c = readNonWsChar(in);

      if (c == ']') {
        break;
      }

      if (c != ',') {
        throw std::runtime_error("Unable to parse");
      }
    }
  }

  if (type != JsonValue::ARRAY) {
    destructValue();
    type = JsonValue::NIL;
    new(valueArray)JsonValue::Array;
    type = JsonValue::ARRAY;
  }

  reinterpret_cast<JsonValue::Array*>(valueArray)->swap(value);
}

void JsonValue::readTrue(std::istream& in) {
  char data[3];
  in.read(data, 3);
  if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') {
    throw std::runtime_error("Unable to parse");
  }

  if (type != JsonValue::BOOL) {
    destructValue();
    type = JsonValue::BOOL;
  }

  valueBool = true;
}

void JsonValue::readFalse(std::istream& in) {
  char data[4];
  in.read(data, 4);
  if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') {
    throw std::runtime_error("Unable to parse");
  }

  if (type != JsonValue::BOOL) {
    destructValue();
    type = JsonValue::BOOL;
  }

  valueBool = false;
}

void JsonValue::readNull(std::istream& in) {
  char data[3];
  in.read(data, 3);
  if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') {
    throw std::runtime_error("Unable to parse");
  }

  if (type != JsonValue::NIL) {
    destructValue();
    type = JsonValue::NIL;
  }
}

void JsonValue::readNumber(std::istream& in, char c) {
  std::string text;
  text += c;
  size_t dots = 0;
  for (;;) {
    int i = in.peek();
    if (i >= '0' && i <= '9') {
      in.read(&c, 1);
      text += c;
    } else  if (i == '.') {
      in.read(&c, 1);
      text += '.';
      ++dots;
    } else {
      break;
    }
  }

  if (dots > 0) {
    if (dots > 1) {
      throw std::runtime_error("Unable to parse");
    }

    int i = in.peek();
    if (in.peek() == 'e') {
      in.read(&c, 1);
      text += c;
      i = in.peek();
      if (i == '+') {
        in.read(&c, 1);
        text += c;
        i = in.peek();
      } else if (i == '-') {
        in.read(&c, 1);
        text += c;
        i = in.peek();
      }

      if (i < '0' || i > '9') {
        throw std::runtime_error("Unable to parse");
      }

      do {
        in.read(&c, 1);
        text += c;
        i = in.peek();
      } while (i >= '0' && i <= '9');
    }

    Real value;
    std::istringstream(text) >> value;
    if (type != REAL) {
      destructValue();
      type = REAL;
    }

    valueReal = value;
  } else {
    if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) {
      throw std::runtime_error("Unable to parse");
    }

    Integer value;
    std::istringstream(text) >> value;
    if (type != INTEGER) {
      destructValue();
      type = INTEGER;
    }

    valueInteger = value;
  }
}

void JsonValue::readObject(std::istream& in) {
  char c = readNonWsChar(in);
  JsonValue::Object value;

  if (c != '}') {
    std::string name;

    for (;;) {
      if (c != '"') {
        throw std::runtime_error("Unable to parse");
      }

      name = readStringToken(in);
      c = readNonWsChar(in);

      if (c != ':') {
        throw std::runtime_error("Unable to parse");
      }

      in >> value[name];
      c = readNonWsChar(in);

      if (c == '}') {
        break;
      }

      if (c != ',') {
        throw std::runtime_error("Unable to parse");
      }

      c = readNonWsChar(in);
    }
  }

  if (type != JsonValue::OBJECT) {
    destructValue();
    type = JsonValue::NIL;
    new(valueObject)JsonValue::Object;
    type = JsonValue::OBJECT;
  }

  reinterpret_cast<JsonValue::Object*>(valueObject)->swap(value);
}

void JsonValue::readString(std::istream& in) {
  String value = readStringToken(in);

  if (type != JsonValue::STRING) {
    destructValue();
    type = JsonValue::NIL;
    new(valueString)String;
    type = JsonValue::STRING;
  }

  reinterpret_cast<String*>(valueString)->swap(value);
}

}
