#ifndef SASS_EXPAND_H
#define SASS_EXPAND_H

#include <vector>

#include "ast.hpp"
#include "eval.hpp"
#include "operation.hpp"
#include "environment.hpp"

namespace Sass {

  class Listize;
  class Context;
  class Eval;
  struct Backtrace;

  class Expand : public Operation_CRTP<Statement*, Expand> {
  public:

    Env* environment();
    SelectorListObj& selector();
    SelectorListObj& original();
    SelectorListObj popFromSelectorStack();
    SelectorStack getOriginalStack();
    SelectorStack getSelectorStack();
    void pushNullSelector();
    void popNullSelector();
    void pushToSelectorStack(SelectorListObj selector);

    SelectorListObj popFromOriginalStack();

    void pushToOriginalStack(SelectorListObj selector);

    Context&          ctx;
    Backtraces&       traces;
    Eval              eval;
    size_t            recursions;
    bool              in_keyframes;
    bool              at_root_without_rule;
    bool              old_at_root_without_rule;

    // it's easier to work with vectors
    EnvStack      env_stack;
    BlockStack    block_stack;
    CallStack     call_stack;
  private:
    SelectorStack selector_stack;
  public:
    SelectorStack originalStack;
    MediaStack    mediaStack;

    Boolean_Obj bool_true;

  private:

    sass::vector<CssMediaQuery_Obj> mergeMediaQueries(const sass::vector<CssMediaQuery_Obj>& lhs, const sass::vector<CssMediaQuery_Obj>& rhs);

  public:
    Expand(Context&, Env*, SelectorStack* stack = nullptr, SelectorStack* original = nullptr);
    ~Expand() { }

    Block* operator()(Block*);
    Statement* operator()(StyleRule*);

    Statement* operator()(MediaRule*);

    // Css StyleRule is already static
    // Statement* operator()(CssMediaRule*);

    Statement* operator()(SupportsRule*);
    Statement* operator()(AtRootRule*);
    Statement* operator()(AtRule*);
    Statement* operator()(Declaration*);
    Statement* operator()(Assignment*);
    Statement* operator()(Import*);
    Statement* operator()(Import_Stub*);
    Statement* operator()(WarningRule*);
    Statement* operator()(ErrorRule*);
    Statement* operator()(DebugRule*);
    Statement* operator()(Comment*);
    Statement* operator()(If*);
    Statement* operator()(ForRule*);
    Statement* operator()(EachRule*);
    Statement* operator()(WhileRule*);
    Statement* operator()(Return*);
    Statement* operator()(ExtendRule*);
    Statement* operator()(Definition*);
    Statement* operator()(Mixin_Call*);
    Statement* operator()(Content*);

    void append_block(Block*);

  };

}

#endif