/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Slice.h"
#include "td/utils/SharedSlice.h"
#include "td/utils/StackAllocator.h"
#include "td/utils/Status.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/tl_storers.h"
#include "td/utils/Variant.h"
#include 
#include 
#define BEGIN_STORE_FLAGS()     \
  do {                          \
    td::uint32 flags_store = 0; \
  td::uint32 bit_offset_store = 0
#define STORE_FLAG(flag)                     \
  flags_store |= (flag) << bit_offset_store; \
  bit_offset_store++
#define END_STORE_FLAGS()         \
  CHECK(bit_offset_store < 31);   \
  td::store(flags_store, storer); \
  }                               \
  while (false)
#define BEGIN_PARSE_FLAGS()          \
  do {                               \
    td::uint32 flags_parse;          \
    td::uint32 bit_offset_parse = 0; \
  td::parse(flags_parse, parser)
#define PARSE_FLAG(flag)                               \
  flag = ((flags_parse >> bit_offset_parse) & 1) != 0; \
  bit_offset_parse++
#define END_PARSE_FLAGS()                                                                                           \
  CHECK(bit_offset_parse < 31);                                                                                     \
  if ((flags_parse & ~((1 << bit_offset_parse) - 1)) != 0) {                                                        \
    parser.set_error(PSTRING() << "Invalid flags " << flags_parse << " left, current bit is " << bit_offset_parse); \
  }                                                                                                                 \
  }                                                                                                                 \
  while (false)
namespace td {
template 
void store(bool x, StorerT &storer) {
  storer.store_binary(static_cast(x));
}
template 
void parse(bool &x, ParserT &parser) {
  x = parser.fetch_int() != 0;
}
template 
void store(int32 x, StorerT &storer) {
  storer.store_binary(x);
}
template 
void parse(int32 &x, ParserT &parser) {
  x = parser.fetch_int();
}
template 
void store(uint32 x, StorerT &storer) {
  storer.store_binary(x);
}
template 
void parse(uint32 &x, ParserT &parser) {
  x = static_cast(parser.fetch_int());
}
template 
void store(int64 x, StorerT &storer) {
  storer.store_binary(x);
}
template 
void parse(int64 &x, ParserT &parser) {
  x = parser.fetch_long();
}
template 
void store(uint64 x, StorerT &storer) {
  storer.store_binary(x);
}
template 
void parse(uint64 &x, ParserT &parser) {
  x = static_cast(parser.fetch_long());
}
template 
void store(double x, StorerT &storer) {
  storer.store_binary(x);
}
template 
void parse(double &x, ParserT &parser) {
  x = parser.fetch_double();
}
template 
void store(Slice x, StorerT &storer) {
  storer.store_string(x);
}
template 
void store(const string &x, StorerT &storer) {
  storer.store_string(x);
}
template 
void store(const SecureString &x, StorerT &storer) {
  storer.store_string(x.as_slice());
}
template 
void parse(string &x, ParserT &parser) {
  x = parser.template fetch_string();
}
template 
void parse(SecureString &x, ParserT &parser) {
  x = parser.template fetch_string();
}
template 
void store(const vector &vec, StorerT &storer) {
  storer.store_binary(narrow_cast(vec.size()));
  for (auto &val : vec) {
    store(val, storer);
  }
}
template 
void parse(vector &vec, ParserT &parser) {
  uint32 size = parser.fetch_int();
  if (parser.get_left_len() < size) {
    parser.set_error("Wrong vector length");
    return;
  }
  vec = vector(size);
  for (auto &val : vec) {
    parse(val, parser);
  }
}
template 
void store(const std::unordered_set &s, StorerT &storer) {
  storer.store_binary(narrow_cast(s.size()));
  for (auto &val : s) {
    store(val, storer);
  }
}
template 
void parse(std::unordered_set &s, ParserT &parser) {
  uint32 size = parser.fetch_int();
  if (parser.get_left_len() < size) {
    parser.set_error("Wrong set length");
    return;
  }
  s.clear();
  for (uint32 i = 0; i < size; i++) {
    Key val;
    parse(val, parser);
    s.insert(std::move(val));
  }
}
template 
std::enable_if_t::value> store(const T &val, StorerT &storer) {
  store(static_cast(val), storer);
}
template 
std::enable_if_t::value> parse(T &val, ParserT &parser) {
  int32 result;
  parse(result, parser);
  val = static_cast(result);
}
template 
std::enable_if_t::value> store(const T &val, StorerT &storer) {
  val.store(storer);
}
template 
std::enable_if_t::value> parse(T &val, ParserT &parser) {
  val.parse(parser);
}
template 
void store(const Variant &variant, StorerT &storer) {
  store(variant.get_offset(), storer);
  variant.visit([&storer](auto &&value) {
    using td::store;
    store(value, storer);
  });
}
template 
void parse(Variant &variant, ParserT &parser) {
  auto type_offset = parser.fetch_int();
  if (type_offset < 0 || type_offset >= static_cast(sizeof...(Types))) {
    return parser.set_error("Invalid type");
  }
  variant.for_each([type_offset, &parser, &variant](int offset, auto *ptr) {
    using T = std::decay_t;
    if (offset == type_offset) {
      variant = T();
      parse(variant.template get(), parser);
    }
  });
}
template 
string serialize(const T &object) {
  TlStorerCalcLength calc_length;
  store(object, calc_length);
  size_t length = calc_length.get_length();
  string key(length, '\0');
  if (!is_aligned_pointer<4>(key.data())) {
    auto ptr = StackAllocator::alloc(length);
    MutableSlice data = ptr.as_slice();
    TlStorerUnsafe storer(data.ubegin());
    store(object, storer);
    CHECK(storer.get_buf() == data.uend());
    key.assign(data.begin(), data.size());
  } else {
    MutableSlice data = key;
    TlStorerUnsafe storer(data.ubegin());
    store(object, storer);
    CHECK(storer.get_buf() == data.uend());
  }
  return key;
}
template 
SecureString serialize_secure(const T &object) {
  TlStorerCalcLength calc_length;
  store(object, calc_length);
  size_t length = calc_length.get_length();
  SecureString key(length, '\0');
  CHECK(is_aligned_pointer<4>(key.data()));
  MutableSlice data = key.as_mutable_slice();
  TlStorerUnsafe storer(data.ubegin());
  store(object, storer);
  CHECK(storer.get_buf() == data.uend());
  return key;
}
template 
TD_WARN_UNUSED_RESULT Status unserialize(T &object, Slice data) {
  TlParser parser(data);
  parse(object, parser);
  parser.fetch_end();
  return parser.get_status();
}
}  // namespace td