/** \file tb2variable.hpp
 *  \brief Abstract Variable class extended with unary costs.
 *
 */

#ifndef TB2VARIABLE_HPP_
#define TB2VARIABLE_HPP_

#include "utils/tb2btlist.hpp"
#include "utils/tb2queue.hpp"
#include "tb2domain.hpp"

/*
 * Main class
 *
 */

class Variable : public WCSPLink {
protected:
    string name;
    int dac; // DAC order

    Long timestamp;
    int pos; // current position in the list of unassigned variables

    StoreValue inf;
    StoreValue sup;
    StoreInt fulleac; // =1 if assigning this variable and all its neighbors to their EAC support value has a zero cost, =0 otherwise

    ConstraintList constrs;
    // ConstraintList triangles;

    // incremental NC data
    StoreCost maxCost;
    StoreValue maxCostValue;
    StoreInt NCBucket;
    DLink<Variable*> linkNCBucket;

    DLink<VariableWithTimeStamp> linkNCQueue;
    DLink<VariableWithTimeStamp> linkIncDecQueue;
    DLink<VariableWithTimeStamp> linkEliminateQueue;

    void setMaxUnaryCost(Value a, Cost cost);
    void changeNCBucket(int newBucket);
    void conflict();

    // make it private because we don't want copy nor assignment
    Variable(const Variable& x);
    Variable& operator=(const Variable& x);

    friend class VACExtension;

public:
    Variable(WCSP* w, string n, Value iinf, Value isup);

    virtual ~Variable() {}

    virtual bool enumerated() const = 0;

    string getName() const { return name; }
    int getDACOrder() const { return dac; }
    void setDACOrder(int order) { dac = order; }
    Value getInf() const { return inf; }
    Value getSup() const { return sup; }
    Value getValue() const
    {
        assert(assigned());
        return inf;
    }
    virtual const string& getValueName(int index) const
    {
        static const string None = std::string("");
        return None;
    }
    virtual unsigned int getDomainSize() const = 0;
    int getCurrentVarId();
    void setCurrentVarId(int idx);

    bool assigned() const { return inf == sup; }
    bool unassigned() const { return inf != sup; }
    virtual bool canbe(Value v) const { return v >= inf && v <= sup; }
    virtual bool cannotbe(Value v) const { return v < inf || v > sup; }

    virtual void increase(Value newInf, bool isDecision = false) = 0;
    virtual void decrease(Value newSup, bool isDecision = false) = 0;
    virtual void remove(Value remValue, bool isDecision = false) = 0;
    virtual void assign(Value newValue, bool isDecision = false) = 0;
    virtual void assignLS(Value newValue, ConstraintSet& delayedCtrs, bool force = false) = 0;

    //    ConstraintList *getTriangles() {return &triangles;}
    ConstraintList* getConstrs() { return &constrs; }
    int getDegree() { return constrs.getSize(); }
    int getTrueDegree();
    Double getMaxElimSize(); ///< \brief returns estimated size of the resulting cost function (including this variable) to eliminate itself
    Long getWeightedDegree();
    DLink<ConstraintLink>* link(Constraint* c, int index);
    void sortConstraints();
    virtual void shrink(){};
    virtual void eliminate(){};

    bool isGlobal();
    BinaryConstraint* getConstr(Variable* x);
    TernaryConstraint* getConstr(Variable* x, Variable* y);
    TernaryConstraint* existTernary();
    double strongLinkedby(Variable*& strvar, TernaryConstraint*& tctr1, TernaryConstraint*& tctr2);
    void deconnect(DLink<ConstraintLink>* link, bool reuse = false);
    void deconnect(); ///< \brief deconnects temporally a variable from the problem (i.e., deconnects all its constraints and assigns to its support value)

    void projectLB(Cost cost);

    virtual Cost getInfCost() const = 0;
    virtual Cost getSupCost() const = 0;
    virtual void projectInfCost(Cost cost) = 0;
    virtual void projectSupCost(Cost cost) = 0;
    virtual Cost getCost(const Value value) const = 0;

    virtual Value getSupport() const { return inf; } // If there is no defined support then return inf
    bool isFullEAC() const { return fulleac; }
    void setFullEAC() { fulleac = 1; }
    void unsetFullEAC() { fulleac = 0; }

    Cost getMaxCost() const { return maxCost; }
    void setMaxCost(Cost cost) { maxCost = cost; }
    Value getMaxCostValue() const { return maxCostValue; }
    void setMaxCostValue(Value value) { maxCostValue = value; }

    virtual void propagateNC() = 0;
    virtual bool verifyNC() = 0;
    virtual bool isEAC() { return true; }
    virtual bool verifyDEE() { return true; }

    void queueNC();
    void queueInc();
    void queueDec();
    void queueEliminate();
    virtual void queueDEE() {}

    void propagateIncDec(int incdec);

    /**********************************************************************/
    //   added for tree decomposition stuff
    int cluster;
    void setCluster(int c)
    {
        assert(cluster == -1 || Store::getDepth() == 0);
        cluster = c;
    }
    int getCluster() { return cluster; }

    BinaryConstraint* getConstrNotDuplicate(Variable* x);
    BinaryConstraint* getConstr(Variable* x, int cid);
    TernaryConstraint* getConstr(Variable* x, Variable* y, int cid);

    bool isSep_;
    void setSep() { isSep_ = true; }
    bool isSep();

    typedef set<pair<int, int>> TSepLink; // set of pairs <cluster in which the variable appears,
    //  			    position of the variable in the delta structure>
    TSepLink clusters;

    void addCluster(int c, int pos)
    {
        clusters.insert(pair<int, int>(c, pos));
    }

    TSepLink::iterator itclusters;

    int nbSeparators() { return clusters.size(); }

    void beginCluster() { itclusters = clusters.begin(); }

    bool nextCluster(int& c, int& pos)
    {
        if (itclusters != clusters.end()) {
            c = (*itclusters).first;
            pos = (*itclusters).second;
            ++itclusters;
            return true;
        } else
            return false;
    }

    /*
    class iterator;
    friend class iterator;
    class iterator {
    public:
        virtual Value operator*() const =0;

        virtual iterator &operator++() =0;     // Prefix form
        virtual iterator &operator--() =0;     // Prefix form

        // To see if you're at the end:
        virtual bool operator==(const iterator &iter) const =0;
        virtual bool operator!=(const iterator &iter) const =0;
    };
    virtual iterator begin() =0;
    virtual iterator end() =0;
    virtual iterator rbegin() =0;
    virtual iterator rend() =0;

    //Finds the first available element whose value is greater or equal to v
    virtual iterator lower_bound(Value v) =0;

    //Finds the first available element whose value is lower or equal to v
    virtual iterator upper_bound(Value v) =0;
     */

    virtual void print(ostream& os) = 0;

    friend ostream& operator<<(ostream& os, Variable& var);
};

#endif /*TB2VARIABLE_HPP_*/

/* Local Variables: */
/* c-basic-offset: 4 */
/* tab-width: 4 */
/* indent-tabs-mode: nil */
/* c-default-style: "k&r" */
/* End: */
