#include <list>
#include <queue>
#include <string>

#ifndef ctx_HEADER
#define ctx_HEADER

template <class T> class ctx_Controller {
  std::list<std::function<void(T)> > callbacks;
  std::list<std::function<T(T)> > functions;
  std::queue<T> queue;

  public:
    T context;
    bool isProcessingQueue = false;

    ctx_Controller(const T &c) {
      context = c;
    }

    void executeFunctions() {
      auto c = queue.front();
      queue.pop();
      for (const auto &f : functions) {
        auto ctx = f(c);
        if (ctx.recentField != "none") {
          queue.push(ctx);
        }
      }

      context.recentField = c.recentField;
      context.setField(c.recentField, c.field(c.recentField));
      reportContext();
    }

    void processQueue() {
      // Decline recursion.
      if (isProcessingQueue) {
        return;
      }
      isProcessingQueue = true;
      while (!queue.empty()) {
        executeFunctions();
      }
      isProcessingQueue = false;
    }

    void registerCallback(std::function<void(T)> cb) {
      callbacks.push_back(cb);
    }

    void registerFieldCallback(
      const std::string &fieldName,
      std::function<void(T)> cb
    ) {
      auto execCB = [fieldName, cb](T c) {
        if (c.recentField == fieldName) {
          cb(c);
        }
      };
      callbacks.push_back(execCB);
    }

    void registerFunction(std::function<T(T)> f) {
      functions.push_back(f);
    }

    void registerFunctions(std::list<std::function<T(T)> > funcs) {
      for (const auto &f : funcs) {
        functions.push_back(f);
      }
    }

    void reportContext() {
      for (const auto &cb : callbacks) {
        cb(context);
      }
    }

    template <typename U> void set(
      const std::string &fieldName,
      const U &value
    ) {
      auto c = context;
      c.setField(fieldName, value);
      c.recentField = fieldName;
      queue.push(c);
      processQueue();
    }
};

#endif // ctx_HEADER
