
#ifndef PLS_DATAFLOW_INTERNAL_INPUTS_H_
#define PLS_DATAFLOW_INTERNAL_INPUTS_H_

#include <tuple>
#include <array>

#include "input.h"

namespace pls {
namespace dataflow {
namespace internal {

template<int P, typename ...I>
class inputs : push_token_cb {
  using values_type = std::tuple<input<P, I>...>;
  values_type values_;
  static constexpr unsigned int num_values = std::tuple_size<values_type>::value;

  std::array<std::atomic<unsigned int>, P> required_inputs_;

 public:
  inputs() {
    for (int i = 0; i < P; i++) {
      required_inputs_[i] = num_values;
    }
    init_cb<0, I...>::call(this);
  }

  void token_pushed(int /*pos*/, token_color color) override {
    int current_required = --required_inputs_[color.get_index(P)];
    if (current_required == 0) {
      std::cout << "All Inputs Avaliable" << std::endl; // TODO: Add proper callback in here
    }
  }

  template<int N>
  typename std::tuple_element<N, values_type>::type &get() {
    return std::get<N>(values_);
  }

// TODO: Change CB code using proper templating to save method calls during execution...
  template<int POS, typename ...TAIL>
  struct init_cb {
    static void call(inputs<P, I...> *inputs) { }
  };
  template<int POS, typename HEAD, typename ...TAIL>
  struct init_cb<POS, HEAD, TAIL...> {
    static void call(inputs<P, I...> *inputs) {
      inputs->get<POS>().set_cb(inputs, POS);
      init_cb<POS + 1, TAIL...>::call(inputs);
    }
  };
};

}
}
}

#endif //PLS_DATAFLOW_INTERNAL_INPUTS_H_
