// #include <vector>

#ifndef CONS_PROP_H
#define CONS_PROP_H

#include <boost/unordered_set.hpp>
#include "MDD.h"

typedef std::pair<MDDNode*, MDDNode*> node_pair;
typedef std::pair<node_pair, node_pair> edge_pair;

class ConstraintPropagation{
private:
  MDD* mdd0;
  MDD* mdd1;

  bool should_be_fwd_mutexed(MDDNode*, MDDNode*);

  bool should_be_fwd_mutexed(MDDNode* node_a, MDDNode* node_a_to,
                         MDDNode* node_b, MDDNode* node_b_to);

  bool should_be_bwd_mutexed(MDDNode*, MDDNode*);
  bool should_be_bwd_mutexed(MDDNode* node_a, MDDNode* node_a_to,
                             MDDNode* node_b, MDDNode* node_b_to);


  void add_bwd_node_mutex(MDDNode* node_a, MDDNode* node_b);

  void add_fwd_node_mutex(MDDNode* node_a, MDDNode* node_b);
  void add_fwd_edge_mutex(MDDNode* node_a, MDDNode* node_a_to,
                      MDDNode* node_b, MDDNode* node_b_to);

public:
  ConstraintPropagation(MDD* mdd0, MDD* mdd1):
    mdd0(mdd0), mdd1(mdd1)
  {}

  boost::unordered_set<edge_pair> fwd_mutexes;
  boost::unordered_set<edge_pair> bwd_mutexes;

  void init_mutex();
  void fwd_mutex_prop();
  // void fwd_mutex_prop_generalized();

  void bwd_mutex_prop();

  bool has_mutex(edge_pair);
  bool has_mutex(MDDNode*, MDDNode*);

  bool has_fwd_mutex(edge_pair);
  bool has_fwd_mutex(MDDNode*, MDDNode*);

  // MDD 0 of level_0 and MDD 1 of level_1 mutexed at goal
  bool mutexed(int level_0, int level_1);
  bool feasible(int level_0, int level_1);
  int _feasible(int level_0, int level_1);

  bool semi_cardinal(int level, int loc);


  std::pair<std::vector<Constraint>, std::vector<Constraint>> generate_constraints(int, int);
};

#endif
