/*
    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 "common/refint.h"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "vm/cellparse.hpp"
#include 
namespace vm {
namespace fmt {
// main idea: use cs >> i(32,x) >> ... or cs >> i32(x) to deserialize integers, instead of cs.write().fetch_long(32, true)
// and cb << i(32,x+y) or cb << i32(x+y) will serialize 32-bit integers
// i, u, i16, u16, i32, u32, ub=u1, ib=i1 for integers
template 
class ConstInt {
  int bits;
  T value;
 public:
  ConstInt(int _bits, T _val) : bits(_bits), value(_val) {
  }
  bool serialize(CellBuilder& cb) const {
    if (C) {
      return (S ? cb.store_long_rchk_bool(value, bits) : cb.store_ulong_rchk_bool(value, bits));
    } else {
      return cb.store_long_bool(value, bits);
    }
  }
  bool deserialize(CellSlice& cs) const {
    if (S) {
      long long x;
      return cs.fetch_long_bool(bits, x) && x == value;
    } else {
      unsigned long long x;
      return cs.fetch_ulong_bool(bits, x) && x == value;
    }
  }
};
template 
class Int {
  int bits;
  T& value;
 public:
  Int(int _bits, T& _val) : bits(_bits), value(_val) {
  }
  bool deserialize(CellSlice& cs) const {
    if (S) {
      long long x;
      if (cs.fetch_long_bool(bits, x)) {
        value = static_cast(x);
        return true;
      }
    } else {
      unsigned long long x;
      if (cs.fetch_ulong_bool(bits, x)) {
        value = static_cast(x);
        return true;
      }
    }
    return false;
  }
  bool serialize(CellBuilder& cb) const {
    if (C) {
      return S ? cb.store_long_rchk_bool(value, bits) : cb.store_ulong_rchk_bool(value, bits);
    } else {
      return cb.store_long_bool(value, bits);
    }
  }
};
template 
class PrefetchInt {
  int bits;
  T& value;
 public:
  PrefetchInt(int _bits, T& _val) : bits(_bits), value(_val) {
  }
  bool deserialize(CellSlice& cs) const {
    if (S) {
      long long x;
      if (cs.prefetch_long_bool(bits, x)) {
        value = static_cast(x);
        return true;
      }
    } else {
      unsigned long long x;
      if (cs.prefetch_ulong_bool(bits, x)) {
        value = static_cast(x);
        return true;
      }
    }
    return false;
  }
};
template 
class ConstRefInt {
  int bits;
  const td::RefInt256& value;
 public:
  ConstRefInt(int _bits, const td::RefInt256& _val) : bits(_bits), value(_val) {
  }
  bool serialize(CellBuilder& cb) const {
    return value.not_null() && cb.store_int256_bool(*value, bits, S);
  }
};
template 
class ConstRefIntVal {
  int bits;
  td::RefInt256 value;
 public:
  ConstRefIntVal(int _bits, const td::RefInt256& _val) : bits(_bits), value(_val) {
  }
  ConstRefIntVal(int _bits, td::RefInt256&& _val) : bits(_bits), value(std::move(_val)) {
  }
  bool serialize(CellBuilder& cb) const {
    return value.not_null() && cb.store_int256_bool(*value, bits, S);
  }
};
template 
class ConstBigInt {
  int bits;
  const td::BigInt256& value;
 public:
  ConstBigInt(int _bits, const td::BigInt256& _val) : bits(_bits), value(_val) {
  }
  bool serialize(CellBuilder& cb) const {
    return cb.store_int256_bool(value, bits, S);
  }
};
template 
class RefInt {
  int bits;
  td::RefInt256& value;
 public:
  RefInt(int _bits, td::RefInt256& _val) : bits(_bits), value(_val) {
  }
  bool deserialize(CellSlice& cs) const {
    value = cs.fetch_int256(bits, S);
    return value.not_null();
  }
  bool serialize(CellBuilder& cb) const {
    return value.not_null() && cb.store_int256_bool(*value, bits, S);
  }
};
inline ConstRefInt i(int l, const td::RefInt256& val) {
  return {l, val};
}
inline ConstRefIntVal i(int l, td::RefInt256&& val) {
  return {l, std::move(val)};
}
inline ConstBigInt i(int l, const td::BigInt256& val) {
  return {l, val};
}
inline RefInt i(int l, td::RefInt256& val) {
  return {l, val};
}
inline ConstRefInt u(int l, const td::RefInt256& val) {
  return {l, val};
}
inline ConstRefIntVal u(int l, td::RefInt256&& val) {
  return {l, std::move(val)};
}
inline ConstBigInt u(int l, const td::BigInt256& val) {
  return {l, val};
}
inline RefInt u(int l, td::RefInt256& val) {
  return {l, val};
}
template 
const ConstInt i(int l, const T& val) {
  return {l, val};
}
template 
Int i(int l, T& val) {
  return {l, val};
}
template 
PrefetchInt pi(int l, T& val) {
  return {l, val};
}
template 
const ConstInt u(int l, const T& val) {
  return {l, val};
}
template 
Int u(int l, T& val) {
  return {l, val};
}
template 
PrefetchInt pu(int l, T& val) {
  return {l, val};
}
template 
const ConstInt iw(int l, const T& val) {
  return {l, val};
}
template 
Int iw(int l, T& val) {
  return {l, val};
}
inline ConstInt ib(bool flag) {
  return {1, flag};
}
template 
Int ib(T& val) {
  return {1, val};
}
template 
PrefetchInt pib(T& val) {
  return {1, val};
}
inline ConstInt ub(bool flag) {
  return {1, flag};
}
template 
Int ub(T& val) {
  return {1, val};
}
template 
PrefetchInt pub(T& val) {
  return {1, val};
}
inline ConstInt i8(signed char val) {
  return {8, val};
}
template 
Int i8(T& val) {
  return {8, val};
}
inline ConstInt u8(unsigned char val) {
  return {8, val};
}
template 
Int u8(T& val) {
  return {8, val};
}
inline ConstInt i16(short val) {
  return {16, val};
}
template 
Int i16(T& val) {
  static_assert(sizeof(T) >= 2, "i16 needs at least 16-bit integer variable as a result");
  return {16, val};
}
inline ConstInt u16(unsigned short val) {
  return {16, val};
}
template 
Int u16(T& val) {
  static_assert(sizeof(T) >= 2, "u16 needs at least 16-bit integer variable as a result");
  return {16, val};
}
template 
const ConstInt i32(const T& val) {
  return {32, val};
}
template 
Int i32(T& val) {
  static_assert(sizeof(T) >= 4, "i32 needs at least 32-bit integer variable as a result");
  return {32, val};
}
template 
PrefetchInt pi32(T& val) {
  static_assert(sizeof(T) >= 4, "pi32 needs at least 32-bit integer variable as a result");
  return {32, val};
}
template 
const ConstInt u32(const T& val) {
  return {32, val};
}
template 
Int u32(T& val) {
  static_assert(sizeof(T) >= 4, "u32 needs at least 32-bit integer variable as a result");
  return {32, val};
}
template 
PrefetchInt pu32(T& val) {
  static_assert(sizeof(T) >= 4, "pu32 needs at least 32-bit integer variable as a result");
  return {32, val};
}
template 
const ConstInt i64(const T& val) {
  return {64, val};
}
template 
Int i64(T& val) {
  static_assert(sizeof(T) >= 8, "i64 needs 64-bit integer variable as a result");
  return {64, val};
}
template 
const ConstInt u64(const T& val) {
  return {64, val};
}
template 
Int u64(T& val) {
  static_assert(sizeof(T) >= 8, "u64 needs 64-bit integer variable as a result");
  return {64, val};
}
/*
 * 
 *   non-integer types
 * 
 */
// cr(Ref| & cell_ref) for (de)serializing cell references
class ConstCellRef {
  const td::Ref& value;
 public:
  ConstCellRef(const td::Ref& _val) : value(_val) {
  }
  bool serialize(CellBuilder& cb) const {
    return cb.store_ref_bool(value);
  }
};
class ConstCellRefVal {
  td::Ref value;
 public:
  ConstCellRefVal(const td::Ref& _val) : value(_val) {
  }
  ConstCellRefVal(td::Ref&& _val) : value(std::move(_val)) {
  }
  bool serialize(CellBuilder& cb) const {
    return cb.store_ref_bool(std::move(value));
  }
};
class CellRefFmt {
  td::Ref& value;
 public:
  CellRefFmt(td::Ref& _val) : value(_val) {
  }
  bool deserialize(CellSlice& cs) const {
    value = cs.fetch_ref();
    return value.not_null();
  }
};
inline ConstCellRef cr(const td::Ref& val) {
  return {val};
}
inline ConstCellRefVal cr(td::Ref&& val) {
  return {std::move(val)};
}
inline CellRefFmt cr(td::Ref& val) {
  return {val};
}
// skip(n) will skip n bits
class SkipFmt {
  int bits;
 public:
  explicit SkipFmt(int _bits) : bits(_bits) {
  }
  bool deserialize(CellSlice& cs) const {
    return cs.advance(bits);
  }
};
inline SkipFmt skip(int bits) {
  return SkipFmt{bits};
}
// end will throw an exception if any bits or references remain, or if a previous operation failed
// ends similar, but checks only bits
class ChkEnd {
 public:
  explicit ChkEnd() = default;
  bool deserialize(CellSlice& cs) const {
    return (cs.empty() && !cs.size_refs());
  }
};
class ChkEndS {
 public:
  explicit ChkEndS() = default;
  bool deserialize(CellSlice& cs) const {
    return cs.empty();
  }
};
template 
class Chk {
  Cond cond;
 public:
  template 
  explicit constexpr Chk(Args... args) : cond(args...){};
  bool deserialize_ext(CellSlice& cs, bool state) const {
    if (!state || !cond.deserialize(cs)) {
      cs.error();
    }
    return true;
  }
};
class ChkOk {
 public:
  explicit ChkOk() = default;
  bool deserialize_ext(CellSlice& cs, bool state) const {
    if (!state) {
      cs.error();
    }
    return true;
  }
};
constexpr ChkEnd end = ChkEnd{};
constexpr ChkEndS ends = ChkEndS{};
constexpr Chk okend = Chk{};
constexpr Chk okends = Chk{};
constexpr Chk oke = Chk{};
constexpr ChkOk ok = ChkOk{};
inline ::vm::CellParser parser(CellSlice& cs) {
  return ::vm::CellParser{cs};
}
}  // namespace fmt
}  // namespace vm |