/*
    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 2020 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "common/refint.h"
#include "vm/stack.hpp"
namespace fift {
using td::Ref;
struct IntCtx;
/*
 * 
 *    FIFT CONTINUATIONS
 * 
 */
class FiftCont : public td::CntObject {
 public:
  FiftCont() = default;
  virtual ~FiftCont() override = default;
  virtual Ref run_tail(IntCtx& ctx) const = 0;
  virtual Ref run_modify(IntCtx& ctx) {
    return run_tail(ctx);
  }
  virtual Ref handle_tail(IntCtx& ctx) const {
    return {};
  }
  virtual Ref handle_modify(IntCtx& ctx) {
    return handle_tail(ctx);
  }
  virtual Ref up() const {
    return {};
  }
  virtual bool is_list() const {
    return false;
  }
  virtual long long list_size() const {
    return -1;
  }
  virtual const Ref* get_list() const {
    return nullptr;
  }
  virtual bool is_literal() const {
    return false;
  }
  virtual int literal_count() const {
    return -1;
  }
  virtual std::vector get_literals() const {
    return {};
  }
  std::string get_dict_name(const IntCtx& ctx) const;
  bool print_dict_name(std::ostream& os, const IntCtx& ctx) const;
  bool print_dummy_name(std::ostream& os, const IntCtx& ctx) const;
  virtual bool print_name(std::ostream& os, const IntCtx& ctx) const;
  virtual bool dump(std::ostream& os, const IntCtx& ctx) const;
  Ref self() const {
    return Ref{this};
  }
};
class QuitCont : public FiftCont {
  int exit_code;
 public:
  QuitCont(int _exit_code = -1) : exit_code(_exit_code) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  bool print_name(std::ostream& os, const IntCtx& ctx) const override;
};
class SeqCont : public FiftCont {
  Ref first, second;
 public:
  SeqCont(Ref _first, Ref _second) : first(std::move(_first)), second(std::move(_second)) {
  }
  static Ref seq(Ref _first, Ref _second) {
    return _second.is_null() ? std::move(_first) : td::make_ref(std::move(_first), std::move(_second));
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  Ref up() const override {
    return second;
  }
  bool print_name(std::ostream& os, const IntCtx& ctx) const override;
  bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class TimesCont : public FiftCont {
  Ref body, after;
  int count;
 public:
  TimesCont(Ref _body, Ref _after, int _count)
      : body(std::move(_body)), after(std::move(_after)), count(_count) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  Ref up() const override {
    return after;
  }
  bool print_name(std::ostream& os, const IntCtx& ctx) const override;
  bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class UntilCont : public FiftCont {
  Ref body, after;
 public:
  UntilCont(Ref _body, Ref _after) : body(std::move(_body)), after(std::move(_after)) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  bool print_name(std::ostream& os, const IntCtx& ctx) const override;
  bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class WhileCont : public FiftCont {
  Ref cond, body, after;
  bool stage;
 public:
  WhileCont(Ref _cond, Ref _body, Ref _after, bool _stage = false)
      : cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), stage(_stage) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  Ref up() const override {
    return after;
  }
  bool print_name(std::ostream& os, const IntCtx& ctx) const override;
  bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class LoopCont : public FiftCont {
  Ref func, after;
  int state;
 public:
  LoopCont(Ref _func, Ref _after, int _state = 0)
      : func(std::move(_func)), after(std::move(_after)), state(_state) {
  }
  LoopCont(const LoopCont&) = default;
  virtual bool init(IntCtx& ctx) {
    return true;
  }
  virtual bool pre_exec(IntCtx& ctx) {
    return true;
  }
  virtual bool post_exec(IntCtx& ctx) {
    return true;
  }
  virtual bool finalize(IntCtx& ctx) {
    return true;
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  Ref up() const override {
    return after;
  }
  bool print_name(std::ostream& os, const IntCtx& ctx) const override;
};
class GenericLitCont : public FiftCont {
 public:
  bool is_literal() const override {
    return true;
  }
  std::vector get_literals() const override = 0;
  bool print_name(std::ostream& os, const IntCtx& ctx) const override;
};
class SmallIntLitCont : public GenericLitCont {
  long long value_;
 public:
  SmallIntLitCont(long long value) : value_(value) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  std::vector get_literals() const override;
  static Ref literal(long long int_value) {
    return td::make_ref(int_value);
  }
};
class IntLitCont : public GenericLitCont {
  td::RefInt256 value_;
 public:
  IntLitCont(td::RefInt256 value) : value_(std::move(value)) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  std::vector get_literals() const override {
    return {vm::StackEntry(value_)};
  }
  static Ref literal(td::RefInt256 int_value);
  static Ref literal(long long int_value) {
    return SmallIntLitCont::literal(int_value);
  }
};
class LitCont : public GenericLitCont {
  vm::StackEntry value_;
 public:
  LitCont(const vm::StackEntry& value) : value_(value) {
  }
  LitCont(vm::StackEntry&& value) : value_(std::move(value)) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  std::vector get_literals() const override {
    return {value_};
  }
  static Ref literal(vm::StackEntry value);
  static Ref literal(td::RefInt256 int_value) {
    return IntLitCont::literal(std::move(int_value));
  }
  static Ref literal(long long int_value) {
    return SmallIntLitCont::literal(int_value);
  }
};
class MultiLitCont : public GenericLitCont {
  std::vector values_;
 public:
  MultiLitCont(const std::vector& values) : values_(values) {
  }
  MultiLitCont(std::vector&& values) : values_(std::move(values)) {
  }
  MultiLitCont(std::initializer_list value_list) : values_(value_list) {
  }
  Ref run_tail(IntCtx& ctx) const override;
  Ref run_modify(IntCtx& ctx) override;
  std::vector get_literals() const override {
    return values_;
  }
  MultiLitCont& push_back(vm::StackEntry new_literal);
  vm::StackEntry at(int idx) const;
};
class InterpretCont : public FiftCont {
 public:
  InterpretCont() = default;
  Ref run_tail(IntCtx&) const override;  // text interpreter, defined in words.cpp
  bool print_name(std::ostream& os, const IntCtx& ctx) const override {
    os << "";
    return true;
  }
};
}  // namespace fift