package ifs.model;

import ifs.util.*;
import java.util.*;

/**
 * Generic value.
 * <br><br>
 * Every value has a notion about the variable it belongs to. It has also a unique id.
 * By default, every Value has an integer value which is used in general heuristics,
 * the task is than to minimimize the total value of assigned values in the solution.
 *
 * @see Variable
 * @see Model
 * @see ifs.solver.Solver
 *
 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomas Muller</a>
 * @version 1.0
 */
public class Value implements Comparable {
    private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Value.class);
    private static IdGenerator sIdGenerator = new IdGenerator();
    
    private long iId;
    private Variable iVariable = null;
    
    private long iAssignmentCounter = 0;
    private long iLastAssignmentIteration = -1;
    private long iLastUnassignmentIteration = -1;
    
    /** Integer value */
    protected int iValue = 0;
    /** Extra information which can be used by an IFS extension (see {@link ifs.extension.Extension})*/
    private Object iExtra = null;
    
    /** Constructor
     * @param variable variable which the value belongs to
     */
    public Value(Variable variable) {
        iId = sIdGenerator.newId();
        iVariable = variable;
    }

    /** Constructor
     * @param variable variable which the value belongs to
     * @param value integer value
     */
    public Value(Variable variable, int value) {
        iId = sIdGenerator.newId();
        iVariable = variable;
        iValue = value;
    }
    
    /** Returns the variable which this value belongs to */
    public Variable variable() { return iVariable; }
    /** Sets the variable which this value belongs to */
    public void setVariable(Variable variable) { iVariable = variable; }
    
    /** Notification (called by variable) that this value is assigned
     * @param iteration current iteration
     */
    public void assigned(long iteration) {
        iAssignmentCounter++; iLastAssignmentIteration = iteration;
    }
    
    /** Notification (called by variable) that this value is unassigned
     * @param iteration current iteration
     */
    public void unassigned(long iteration) { iLastUnassignmentIteration = iteration; }
    
    /** Returns the iteration when the value was assigned at last (-1 if never).*/
    public long lastAssignmentIteration() { return iLastAssignmentIteration; }
    /** Returns the iteration when the value was unassigned at last (-1 if never).*/
    public long lastUnassignmentIteration() { return iLastUnassignmentIteration; }
    /** Returns the number of assignments of this value to its variable.*/
    public long countAssignments() { return iAssignmentCounter; }
    
    /** Unique id */
    public long getId() { return iId;}
    
    /** Values name -- for printing purposes (E.g., Monday 7:30)*/
    public String getName() { return String.valueOf(iId); }
    
    /** Values description -- for printing purposes*/
    public String getDescription() { return null; }
    
    /** Integer representaion. This allows us to have generic optimization criteria. The task
     * is than to minimize total value of assigned variables of a solution.
     */
    public int toInt() { return iValue; }
    
    public String toString() { return getName(); }
    
    public int hashCode() { return (int)iId; }
    
    /** Comparison of two values which is based only on the value (not appropriate variable etc.). toInt() is compared by default. */
    public boolean valueEquals(Value value) {
        if (value==null) return false;
        return toInt()==value.toInt();
    }
    
    public int compareTo(Object o) {
        if (o==null || !(o instanceof Value)) return -1;
        return toInt() - ((Value)o).toInt();
    }
    
    /** By default, comparison is made on unique ids */
    public boolean equals(Object o) {
        try {
            return getId()==((Value)o).getId();
        } catch (Exception e) { return false; }
    }
    
    /** Extra information to which can be used by an extension (see {@link ifs.extension.Extension}). */
    public Object getExtra() { return iExtra; }
    /** Extra information to which can be used by an extension (see {@link ifs.extension.Extension}). */
    public void setExtra(Object object) { iExtra = object; }

    /** Returns a set of conflicting values with this value. When empty, the value is consistent with the existing assignment. */
    public java.util.Set conflicts() {
        HashSet conflicts = new HashSet();
        for (Enumeration e1=variable().constraints().elements();e1.hasMoreElements();) {
            Constraint constraint = (Constraint)e1.nextElement();
            constraint.computeConflicts(this, conflicts);
            if (!conflicts.isEmpty()) return conflicts;
        }
        return null;
    }
}
