// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_CRDTP_PROTOCOL_CORE_H_
#define V8_CRDTP_PROTOCOL_CORE_H_

#include <sys/types.h>

#include <memory>
#include <string>
#include <vector>

#include "cbor.h"
#include "maybe.h"
#include "serializable.h"
#include "span.h"
#include "status.h"

namespace v8_crdtp {

class DeserializerState {
 public:
  using Storage = std::shared_ptr<const std::vector<uint8_t>>;

  // Creates a state from the raw bytes received from the peer.
  explicit DeserializerState(std::vector<uint8_t> bytes);
  // Creates the state from the part of another message.
  DeserializerState(Storage storage, span<uint8_t> span);
  DeserializerState(const DeserializerState& r) = delete;
  DeserializerState(DeserializerState&& r) = default;

  // Registers |error|, unless the tokenizer's status is already an error.
  void RegisterError(Error error);
  // Registers |name| as a segment of the field path.
  void RegisterFieldPath(span<char> name);

  // Produces an error message considering |tokenizer.Status()|,
  // status_, and field_path_.
  std::string ErrorMessage(span<char> message_name) const;
  Status status() const;
  const Storage& storage() const { return storage_; }
  cbor::CBORTokenizer* tokenizer() { return &tokenizer_; }

 private:
  const Storage storage_;
  cbor::CBORTokenizer tokenizer_;
  Status status_;
  std::vector<span<char>> field_path_;
};

template <typename T, typename = void>
struct ProtocolTypeTraits {};

template <>
struct ProtocolTypeTraits<bool> {
  static bool Deserialize(DeserializerState* state, bool* value);
  static void Serialize(bool value, std::vector<uint8_t>* bytes);
};

template <>
struct ProtocolTypeTraits<int32_t> {
  static bool Deserialize(DeserializerState* state, int* value);
  static void Serialize(int value, std::vector<uint8_t>* bytes);
};

template <>
struct ProtocolTypeTraits<double> {
  static bool Deserialize(DeserializerState* state, double* value);
  static void Serialize(double value, std::vector<uint8_t>* bytes);
};

class ContainerSerializer {
 public:
  ContainerSerializer(std::vector<uint8_t>* bytes, uint8_t tag);

  template <typename T>
  void AddField(span<char> field_name, const T& value) {
    cbor::EncodeString8(
        span<uint8_t>(reinterpret_cast<const uint8_t*>(field_name.data()),
                      field_name.size()),
        bytes_);
    ProtocolTypeTraits<T>::Serialize(value, bytes_);
  }
  template <typename T>
  void AddField(span<char> field_name, const detail::ValueMaybe<T>& value) {
    if (!value.isJust())
      return;
    AddField(field_name, value.fromJust());
  }
  template <typename T>
  void AddField(span<char> field_name, const detail::PtrMaybe<T>& value) {
    if (!value.isJust())
      return;
    AddField(field_name, *value.fromJust());
  }

  void EncodeStop();

 private:
  std::vector<uint8_t>* const bytes_;
  cbor::EnvelopeEncoder envelope_;
};

class ObjectSerializer {
 public:
  ObjectSerializer();
  ~ObjectSerializer();

  template <typename T>
  void AddField(span<char> name, const T& field) {
    serializer_.AddField(name, field);
  }
  std::unique_ptr<Serializable> Finish();

 private:
  std::vector<uint8_t> owned_bytes_;
  ContainerSerializer serializer_;
};

class DeserializerDescriptor {
 public:
  struct Field {
    span<char> name;
    bool is_optional;
    bool (*deserializer)(DeserializerState* state, void* obj);
  };

  DeserializerDescriptor(const Field* fields, size_t field_count);

  bool Deserialize(DeserializerState* state, void* obj) const;

 private:
  bool DeserializeField(DeserializerState* state,
                        span<char> name,
                        int* seen_mandatory_fields,
                        void* obj) const;

  const Field* const fields_;
  const size_t field_count_;
  const int mandatory_field_mask_;
};

template <typename T>
struct ProtocolTypeTraits<std::vector<T>> {
  static bool Deserialize(DeserializerState* state, std::vector<T>* value) {
    auto* tokenizer = state->tokenizer();
    if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE)
      tokenizer->EnterEnvelope();
    if (tokenizer->TokenTag() != cbor::CBORTokenTag::ARRAY_START) {
      state->RegisterError(Error::CBOR_ARRAY_START_EXPECTED);
      return false;
    }
    assert(value->empty());
    tokenizer->Next();
    for (; tokenizer->TokenTag() != cbor::CBORTokenTag::STOP;
         tokenizer->Next()) {
      value->emplace_back();
      if (!ProtocolTypeTraits<T>::Deserialize(state, &value->back()))
        return false;
    }
    return true;
  }

  static void Serialize(const std::vector<T>& value,
                        std::vector<uint8_t>* bytes) {
    ContainerSerializer container_serializer(
        bytes, cbor::EncodeIndefiniteLengthArrayStart());
    for (const auto& item : value)
      ProtocolTypeTraits<T>::Serialize(item, bytes);
    container_serializer.EncodeStop();
  }
};

template <typename T>
struct ProtocolTypeTraits<std::unique_ptr<std::vector<T>>> {
  static bool Deserialize(DeserializerState* state,
                          std::unique_ptr<std::vector<T>>* value) {
    auto res = std::make_unique<std::vector<T>>();
    if (!ProtocolTypeTraits<std::vector<T>>::Deserialize(state, res.get()))
      return false;
    *value = std::move(res);
    return true;
  }
  static void Serialize(const std::unique_ptr<std::vector<T>>& value,
                        std::vector<uint8_t>* bytes) {
    ProtocolTypeTraits<std::vector<T>>::Serialize(*value, bytes);
  }
};

class DeferredMessage : public Serializable {
 public:
  static std::unique_ptr<DeferredMessage> FromSerializable(
      std::unique_ptr<Serializable> serializeable);
  static std::unique_ptr<DeferredMessage> FromSpan(span<uint8_t> bytes);

  ~DeferredMessage() override = default;
  virtual DeserializerState MakeDeserializer() const = 0;

 protected:
  DeferredMessage() = default;
};

template <>
struct ProtocolTypeTraits<std::unique_ptr<DeferredMessage>> {
  static bool Deserialize(DeserializerState* state,
                          std::unique_ptr<DeferredMessage>* value);
  static void Serialize(const std::unique_ptr<DeferredMessage>& value,
                        std::vector<uint8_t>* bytes);
};

template <typename T>
struct ProtocolTypeTraits<detail::ValueMaybe<T>> {
  static bool Deserialize(DeserializerState* state,
                          detail::ValueMaybe<T>* value) {
    T res;
    if (!ProtocolTypeTraits<T>::Deserialize(state, &res))
      return false;
    *value = std::move(res);
    return true;
  }

  static void Serialize(const detail::ValueMaybe<T>& value,
                        std::vector<uint8_t>* bytes) {
    ProtocolTypeTraits<T>::Serialize(value.fromJust(), bytes);
  }
};

template <typename T>
struct ProtocolTypeTraits<detail::PtrMaybe<T>> {
  static bool Deserialize(DeserializerState* state,
                          detail::PtrMaybe<T>* value) {
    std::unique_ptr<T> res;
    if (!ProtocolTypeTraits<std::unique_ptr<T>>::Deserialize(state, &res))
      return false;
    *value = std::move(res);
    return true;
  }

  static void Serialize(const detail::PtrMaybe<T>& value,
                        std::vector<uint8_t>* bytes) {
    ProtocolTypeTraits<T>::Serialize(*value.fromJust(), bytes);
  }
};

template <typename T>
class DeserializableProtocolObject {
 public:
  static StatusOr<std::unique_ptr<T>> ReadFrom(
      const DeferredMessage& deferred_message) {
    auto state = deferred_message.MakeDeserializer();
    if (auto res = Deserialize(&state))
      return StatusOr<std::unique_ptr<T>>(std::move(res));
    return StatusOr<std::unique_ptr<T>>(state.status());
  }

  static StatusOr<std::unique_ptr<T>> ReadFrom(std::vector<uint8_t> bytes) {
    auto state = DeserializerState(std::move(bytes));
    if (auto res = Deserialize(&state))
      return StatusOr<std::unique_ptr<T>>(std::move(res));
    return StatusOr<std::unique_ptr<T>>(state.status());
  }

  // Short-hand for legacy clients. This would swallow any errors, consider
  // using ReadFrom.
  static std::unique_ptr<T> FromBinary(const uint8_t* bytes, size_t size) {
    std::unique_ptr<T> value(new T());
    auto deserializer = DeferredMessage::FromSpan(span<uint8_t>(bytes, size))
                            ->MakeDeserializer();
    Deserialize(&deserializer, value.get());
    return value;
  }

  static bool Deserialize(DeserializerState* state, T* value) {
    return T::deserializer_descriptor().Deserialize(state, value);
  }

 protected:
  // This is for the sake of the macros used by derived classes thay may be in
  // a different namespace;
  using ProtocolType = T;
  using DeserializerDescriptorType = DeserializerDescriptor;
  template <typename U>
  using DeserializableBase = DeserializableProtocolObject<U>;

  DeserializableProtocolObject() = default;
  ~DeserializableProtocolObject() = default;

 private:
  friend struct ProtocolTypeTraits<std::unique_ptr<T>>;

  static std::unique_ptr<T> Deserialize(DeserializerState* state) {
    std::unique_ptr<T> value(new T());
    if (Deserialize(state, value.get()))
      return value;
    return nullptr;
  }
};

template <typename T>
class ProtocolObject : public Serializable,
                       public DeserializableProtocolObject<T> {
 public:
  std::unique_ptr<T> Clone() const {
    std::vector<uint8_t> serialized;
    AppendSerialized(&serialized);
    return T::ReadFrom(std::move(serialized)).value();
  }
  // TODO(caseq): compatibility only, remove.
  std::unique_ptr<T> clone() const { return Clone(); }

 protected:
  using ProtocolType = T;

  ProtocolObject() = default;
};

template <typename T>
struct ProtocolTypeTraits<
    T,
    typename std::enable_if<
        std::is_base_of<ProtocolObject<T>, T>::value>::type> {
  static bool Deserialize(DeserializerState* state, T* value) {
    return T::Deserialize(state, value);
  }

  static void Serialize(const T& value, std::vector<uint8_t>* bytes) {
    value.AppendSerialized(bytes);
  }
};

template <typename T>
struct ProtocolTypeTraits<
    std::unique_ptr<T>,
    typename std::enable_if<
        std::is_base_of<ProtocolObject<T>, T>::value>::type> {
  static bool Deserialize(DeserializerState* state, std::unique_ptr<T>* value) {
    std::unique_ptr<T> res = T::Deserialize(state);
    if (!res)
      return false;
    *value = std::move(res);
    return true;
  }

  static void Serialize(const std::unique_ptr<T>& value,
                        std::vector<uint8_t>* bytes) {
    ProtocolTypeTraits<T>::Serialize(*value, bytes);
  }
};

#define DECLARE_DESERIALIZATION_SUPPORT()  \
  friend DeserializableBase<ProtocolType>; \
  static const DeserializerDescriptorType& deserializer_descriptor()

#define DECLARE_SERIALIZATION_SUPPORT()                              \
 public:                                                             \
  void AppendSerialized(std::vector<uint8_t>* bytes) const override; \
                                                                     \
 private:                                                            \
  friend DeserializableBase<ProtocolType>;                           \
  static const DeserializerDescriptorType& deserializer_descriptor()

#define V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, is_optional)  \
  {                                                                \
    MakeSpan(name), is_optional,                                   \
        [](DeserializerState* __state, void* __obj) -> bool {      \
          return ProtocolTypeTraits<decltype(field)>::Deserialize( \
              __state, &static_cast<ProtocolType*>(__obj)->field); \
        }                                                          \
  }

// clang-format off
#define V8_CRDTP_BEGIN_DESERIALIZER(type)                                      \
  const type::DeserializerDescriptorType& type::deserializer_descriptor() { \
    using namespace v8_crdtp;                                                  \
    static const DeserializerDescriptorType::Field fields[] = {

#define V8_CRDTP_END_DESERIALIZER()                    \
    };                                              \
    static const DeserializerDescriptorType s_desc( \
        fields, sizeof fields / sizeof fields[0]);  \
    return s_desc;                                  \
  }

#define V8_CRDTP_DESERIALIZE_FIELD(name, field) \
  V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, false)
#define V8_CRDTP_DESERIALIZE_FIELD_OPT(name, field) \
  V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, true)

#define V8_CRDTP_BEGIN_SERIALIZER(type)                               \
  void type::AppendSerialized(std::vector<uint8_t>* bytes) const { \
    using namespace v8_crdtp;                                         \
    ContainerSerializer __serializer(bytes,                        \
                                     cbor::EncodeIndefiniteLengthMapStart());

#define V8_CRDTP_SERIALIZE_FIELD(name, field) \
    __serializer.AddField(MakeSpan(name), field)

#define V8_CRDTP_END_SERIALIZER() \
    __serializer.EncodeStop();   \
  } class __cddtp_dummy_name
// clang-format on

}  // namespace v8_crdtp

#endif  // V8_CRDTP_PROTOCOL_CORE_H_
