/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include "cpp/HsStruct.h"

#include "cpp/Marshallable.h"

/*
 * HsJSON
 */

HsJSON::HsJSON(const folly::dynamic& value) {
  switch (value.type()) {
    case folly::dynamic::NULLT:
      type = Type::Null;
      break;
    case folly::dynamic::BOOL:
      type = Type::Bool;
      integral = value.asBool();
      break;
    case folly::dynamic::INT64:
      type = Type::Integral;
      integral = value.asInt();
      break;
    case folly::dynamic::DOUBLE:
      type = Type::Real;
      real = value.asDouble();
      break;
    case folly::dynamic::STRING:
      type = Type::String;
      new (&string) HsString(value.asString());
      break;
    case folly::dynamic::ARRAY:
      type = Type::Array;
      {
        std::vector<HsJSON> v;
        v.reserve(value.size());
        for (const folly::dynamic& i : value) {
          v.emplace_back(i);
        }
        new (&array) HsArray<HsJSON>(std::move(v));
      }
      break;
    case folly::dynamic::OBJECT:
      type = Type::Object;
      {
        std::vector<HsString> keys;
        std::vector<HsJSON> values;
        keys.reserve(value.size());
        values.reserve(value.size());
        for (const auto& i : value.items()) {
          keys.emplace_back(i.first.asString());
          values.emplace_back(i.second);
        }
        new (&object) HsObject<HsJSON>(std::move(keys), std::move(values));
      }
      break;
  }
}

folly::dynamic HsJSON::toDynamic() && {
  switch (type) {
    case Type::Null:
      return folly::dynamic();
    case Type::Bool:
      return folly::dynamic(static_cast<bool>(integral));
    case Type::Integral:
      return folly::dynamic(integral);
    case Type::Real:
      return folly::dynamic(real);
    case Type::String:
      return folly::dynamic(std::move(string).toStdString());
    case Type::Array: {
      auto res = folly::dynamic::array();
      auto end = std::make_move_iterator(array.end());
      for (auto itr = std::make_move_iterator(array.begin()); itr != end;
           itr++) {
        res.push_back(std::move(*itr).toDynamic());
      }
      return res;
    }
    case Type::Object: {
      folly::dynamic res = folly::dynamic::object;
      auto items = std::move(object).take_items();

      // Manual version of zipping 2 iterators together
      auto key_itr = std::make_move_iterator(items.first.begin());
      auto key_end = std::make_move_iterator(items.first.end());
      auto val_itr = std::make_move_iterator(items.second.begin());
      auto val_end = std::make_move_iterator(items.second.end());

      while (key_itr != key_end && val_itr != val_end) {
        res.insert(
            std::move(*key_itr).toStdString(), std::move(*val_itr).toDynamic());
        key_itr++;
        val_itr++;
      }
      return res;
    }
  }
  folly::assume_unreachable();
}

void HsJSON::construct(HsJSON&& other) {
  DCHECK(this != &other);
  type = other.type;
  switch (type) {
    case Type::Null:
      break;
    case Type::Bool:
    case Type::Integral:
      integral = other.integral;
      break;
    case Type::Real:
      real = other.real;
      break;
    case Type::String:
      new (&string) HsString(std::move(other.string));
      break;
    case Type::Array:
      new (&array) HsArray<HsJSON>(std::move(other.array));
      break;
    case Type::Object:
      new (&object) HsObject<HsJSON>(std::move(other.object));
      break;
  }
}

void HsJSON::destruct() {
  if (type == Type::String) {
    string.~HsString();
  } else if (type == Type::Array) {
    array.~HsArray();
  } else if (type == Type::Object) {
    object.~HsMap();
  }
}

void ctorHsJSONNull(HsJSON* p) noexcept {
  new (p) HsJSON();
}

void ctorHsJSONBool(HsJSON* p, bool b) noexcept {
  new (p) HsJSON(b);
}

void ctorHsJSONInt(HsJSON* p, int64_t i) noexcept {
  new (p) HsJSON(i);
}

void ctorHsJSONDouble(HsJSON* p, double d) noexcept {
  new (p) HsJSON(d);
}

void ctorHsJSONString(HsJSON* p, HsString* txt) noexcept {
  new (p) HsJSON(std::move(*txt));
}

void ctorHsJSONArray(HsJSON* p, HsArray<HsJSON>* a) noexcept {
  new (p) HsJSON(std::move(*a));
}

void ctorHsJSONObject(HsJSON* p, HsObject<HsJSON>* o) noexcept {
  new (p) HsJSON(std::move(*o));
}

/*
 * HsObject
 */
extern "C" void common_hs_ctorHsObjectJSON(
    HsObject<HsJSON>* p,
    HsArray<HsString>* keys,
    HsArray<HsJSON>* vals) {
  new (p) HsObject<HsJSON>(
      std::move(*keys).toStdVector(), std::move(*vals).toStdVector());
}

/*
 * HsStringPiece
 */

HsStringPiece* newHsStringPiece(const char* p, size_t n) noexcept {
  return new HsStringPiece(p, n);
}

void ctorHsStringPiece(HsRange<char>* ret, const char* p, size_t n) noexcept {
  new (ret) HsStringPiece(p, n);
}

/*
 * HsString
 */

HsString* newHsString(const char* p, size_t n) noexcept {
  return new HsString(std::string(p, n));
}

void ctorHsString(HsString* ret, const char* p, size_t n) noexcept {
  new (ret) HsString(std::string(p, n));
}

/*
 * HsEither
 */

// HsEither<char,char> instead of HsEither<void,void> just to keep class
// definition happy.
void* newHsEither(HsEitherTag tag, void* val) {
  return new DummyHsEither(tag, val);
}

HS_DEFINE_MARSHALLABLE(HsMaybeInt, HsMaybe<int64_t>);
HS_DEFINE_MARSHALLABLE(HsMaybeDouble, HsMaybe<double>);
HS_DEFINE_MARSHALLABLE(HsMaybeString, HsMaybe<HsString>);

HS_DEFINE_MARSHALLABLE(HsEitherStringInt, HsEither<HsString, int64_t>);
HS_DEFINE_MARSHALLABLE(HsEitherStringDouble, HsEither<HsString, double>);
HS_DEFINE_MARSHALLABLE(HsEitherStringString, HsEither<HsString, HsString>);

HS_DEFINE_MARSHALLABLE(
    HsEitherStringArrayInt,
    HsEither<HsString, HsArray<int64_t>>);
HS_DEFINE_MARSHALLABLE(
    HsEitherStringArrayDouble,
    HsEither<HsString, HsArray<double>>);
HS_DEFINE_MARSHALLABLE(
    HsEitherStringArrayString,
    HsEither<HsString, HsArray<HsString>>);

HS_DEFINE_MARSHALLABLE(HsString, HsString);
HS_DEFINE_MARSHALLABLE(HsStringPiece, HsStringPiece);

HS_DEFINE_MARSHALLABLE(HsArrayInt16, HsArray<int16_t>);
HS_DEFINE_MARSHALLABLE(HsArrayInt32, HsArray<int32_t>);
HS_DEFINE_MARSHALLABLE(HsArrayInt64, HsArray<int64_t>);
HS_DEFINE_MARSHALLABLE(HsArrayUInt8, HsArray<uint8_t>);
HS_DEFINE_MARSHALLABLE(HsArrayUInt16, HsArray<uint16_t>);
HS_DEFINE_MARSHALLABLE(HsArrayUInt32, HsArray<uint32_t>);
HS_DEFINE_MARSHALLABLE(HsArrayUInt64, HsArray<uint64_t>);
HS_DEFINE_MARSHALLABLE(HsArrayFloat, HsArray<float>);
HS_DEFINE_MARSHALLABLE(HsArrayDouble, HsArray<double>);
HS_DEFINE_MARSHALLABLE(HsArrayString, HsArray<HsString>);
HS_DEFINE_MARSHALLABLE(HsArrayStringPiece, HsArray<HsStringPiece>);
HS_DEFINE_MARSHALLABLE(HsArrayJSON, HsArray<HsJSON>);

HS_DEFINE_MARSHALLABLE(HsSetInt16, HsSet<int16_t>);
HS_DEFINE_MARSHALLABLE(HsSetInt32, HsSet<int32_t>);
HS_DEFINE_MARSHALLABLE(HsSetInt64, HsSet<int64_t>);
HS_DEFINE_MARSHALLABLE(HsSetUInt8, HsSet<uint8_t>);
HS_DEFINE_MARSHALLABLE(HsSetUInt16, HsSet<uint16_t>);
HS_DEFINE_MARSHALLABLE(HsSetUInt32, HsSet<uint32_t>);
HS_DEFINE_MARSHALLABLE(HsSetUInt64, HsSet<uint64_t>);
HS_DEFINE_MARSHALLABLE(HsSetFloat, HsSet<float>);
HS_DEFINE_MARSHALLABLE(HsSetDouble, HsSet<double>);
HS_DEFINE_MARSHALLABLE(HsSetString, HsSet<HsString>);
HS_DEFINE_MARSHALLABLE(HsSetJSON, HsSet<HsJSON>);

HS_DEFINE_MARSHALLABLE(HsMapIntInt, HsMap<int64_t, int64_t>);
HS_DEFINE_MARSHALLABLE(HsMapUInt8UInt8, HsMap<uint8_t, uint8_t>);
HS_DEFINE_MARSHALLABLE(HsMapUInt16UInt16, HsMap<uint16_t, uint16_t>);
HS_DEFINE_MARSHALLABLE(HsMapInt32Int32, HsMap<int32_t, int32_t>);
HS_DEFINE_MARSHALLABLE(HsMapInt32Double, HsMap<int32_t, double>);
HS_DEFINE_MARSHALLABLE(HsMapIntDouble, HsMap<int64_t, double>);
HS_DEFINE_MARSHALLABLE(HsMapIntString, HsMap<int64_t, HsString>);
HS_DEFINE_MARSHALLABLE(HsMapInt32String, HsMap<int32_t, HsString>);
HS_DEFINE_MARSHALLABLE(HsMapDoubleInt32, HsMap<double, int32_t>);
HS_DEFINE_MARSHALLABLE(HsMapStringInt32, HsMap<HsString, int32_t>);
HS_DEFINE_MARSHALLABLE(HsMapStringInt, HsMap<HsString, int64_t>);
HS_DEFINE_MARSHALLABLE(HsMapStringDouble, HsMap<HsString, double>);
HS_DEFINE_MARSHALLABLE(HsMapStringString, HsMap<HsString, HsString>);

HS_DEFINE_MARSHALLABLE(HsObjectJSON, HsObject<HsJSON>);
HS_DEFINE_MARSHALLABLE(HsJSON, HsJSON);

HS_OPTION_CPP(Bool, bool);
HS_OPTION_CPP(UInt8, uint8_t);
HS_OPTION_CPP(Int16, int16_t);
HS_OPTION_CPP(Int32, int32_t);
HS_OPTION_CPP(Int64, int64_t);
HS_OPTION_CPP(UInt32, uint32_t);
HS_OPTION_CPP(UInt64, uint64_t);
HS_OPTION_CPP(Float, float);
HS_OPTION_CPP(Double, double);
HS_OPTION_CPP(String, HsString);
HS_OPTION_CPP(StringView, HsStringPiece);
HS_OPTION_CPP(HsJSON, HsJSON);

HS_DEFINE_ARRAY_CONSTRUCTIBLE(Int16, int16_t);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(Int32, int32_t);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(Int64, int64_t);
// std::vector<bool> doesn't guarantee contiguous memory layout,
// so use HsArray<uint8_t> to store one bool per byte
HS_DEFINE_ARRAY_CONSTRUCTIBLE(UInt8, uint8_t);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(UInt16, uint16_t);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(UInt32, uint32_t);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(UInt64, uint64_t);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(Float, float);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(Double, double);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(String, HsString);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(StringView, HsStringPiece);
HS_DEFINE_ARRAY_CONSTRUCTIBLE(HsJSON, HsJSON);

HS_DEFINE_MAP_CONSTRUCTIBLE(IntInt, int64_t, int64_t);
HS_DEFINE_MAP_CONSTRUCTIBLE(UInt8UInt8, uint8_t, uint8_t);
HS_DEFINE_MAP_CONSTRUCTIBLE(UInt16UInt16, uint16_t, uint16_t);
HS_DEFINE_MAP_CONSTRUCTIBLE(Int32Int32, int32_t, int32_t);
HS_DEFINE_MAP_CONSTRUCTIBLE(Int32Double, int32_t, double);
HS_DEFINE_MAP_CONSTRUCTIBLE(Int32String, int32_t, HsString);
HS_DEFINE_MAP_CONSTRUCTIBLE(DoubleInt32, double, int32_t);
HS_DEFINE_MAP_CONSTRUCTIBLE(StringInt32, HsString, int32_t);
HS_DEFINE_MAP_CONSTRUCTIBLE(StringDouble, HsString, double);
HS_DEFINE_MAP_CONSTRUCTIBLE(StringString, HsString, HsString);

HS_DEFINE_SET_CONSTRUCTIBLE(Int16, int16_t);
HS_DEFINE_SET_CONSTRUCTIBLE(Int32, int32_t);
HS_DEFINE_SET_CONSTRUCTIBLE(Int64, int64_t);
HS_DEFINE_SET_CONSTRUCTIBLE(UInt8, uint8_t);
HS_DEFINE_SET_CONSTRUCTIBLE(UInt16, uint16_t);
HS_DEFINE_SET_CONSTRUCTIBLE(UInt32, uint32_t);
HS_DEFINE_SET_CONSTRUCTIBLE(UInt64, uint64_t);
HS_DEFINE_SET_CONSTRUCTIBLE(Float, float);
HS_DEFINE_SET_CONSTRUCTIBLE(Double, double);
HS_DEFINE_SET_CONSTRUCTIBLE(String, HsString);
HS_DEFINE_SET_CONSTRUCTIBLE(HsJSON, HsJSON);
