package ttsolver.constraint;

import ifs.model.*;
import ifs.util.*;
import java.util.*;
import ttsolver.model.*;

/**
 * Join student enrollment constraint.
 * <br>
 * This constraint is placed between all pairs of classes where there is at least one student attending both classes.
 * It represents a number of student conflicts (number of joined enrollments), if the given two classes overlap in time.
 * <br>
 * Also, it dynamically maintains the counter of all student conflicts. It is a soft constraint.
 *
 *
 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomas Muller</a>
 * @version 1.0
 */

public class JenrlConstraint extends BinaryConstraint {
    private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(JenrlConstraint.class);
    private Counter iGlobalViolations;
    private long iJenrl;
    private boolean iAdded = false;
    
    /** Constructor
     * @param jenrl number of joined enrollments
     * @param globalViolations global counter of student conflicts
     */
    public JenrlConstraint(long jenrl, Counter globalViolations) {
        super();
        iJenrl = jenrl;
        iGlobalViolations = globalViolations;
    }

    /** Set global counter of student conflicts */
    public void setViolatedCounter(Counter globalViolations) { iGlobalViolations = globalViolations; }
    
    public void computeConflicts(Value value, Set conflicts) {
    }
    
    public boolean inConflict(Value value) {
        return false;
    }
    
    public boolean isConsistent(Value value1, Value value2) {
        return true;
    }
    
    public void unassigned(long iteration, Value value) {
        super.unassigned(iteration, value);
        if (iAdded) {
            iGlobalViolations.dec(iJenrl);
            iAdded=false;
            ((Lecture)first()).removeActiveJenrl(this);
            ((Lecture)second()).removeActiveJenrl(this);
        }
    }
    
    /** Returns true if the given placements are overlapping or they are back-to-back and too far for students.*/
   public static boolean isInConflict(Placement p1, Placement p2) {
       if (p1==null || p2==null) return false;
       TimeLocation t1=p1.getTimeLocation(), t2=p2.getTimeLocation();
       if (!t1.shareDays(t2)) return false;
       if (t1.shareHours(t2)) return true;
       //if (t1.hasIntersection(t2)) return true;
       //if (!Placement.shareDays(p1,p2)) return false;
       int s1 = t1.getStartSlot(), s2 = t2.getStartSlot();
       if (s1+t1.getNrHalfHoursPerMeeting()!=s2 &&
           s2+t2.getNrHalfHoursPerMeeting()!=s1) return false;
       double distance = Placement.getDistance(p1,p2);
       if (distance <= 67.0) return false;
       if (distance <= 100.0 && (
           (t1.getLength()==3 && s1+t1.getLength()==s2) ||
           (t2.getLength()==3 && s2+t2.getLength()==s1)))
           return false;
       return true;
   }

   public void assigned(long iteration, Value value) {
       super.assigned(iteration, value);
       if (second()==null || first().getAssignment()==null || second().getAssignment()==null) return;
        //if (v1.getInitialAssignment()!=null && v2.getInitialAssignment()!=null && v1.getAssignment().equals(v1.getInitialAssignment()) && v2.getAssignment().equals(v2.getInitialAssignment())) return;
        if (isInConflict((Placement)first().getAssignment(),(Placement)second().getAssignment())) {
            iAdded=true;
            iGlobalViolations.inc(iJenrl);
            ((Lecture)first()).addActiveJenrl(this);
            ((Lecture)second()).addActiveJenrl(this);
        }
    }

   /** Number of joined enrollments if the given value is assigned to the given variable */
    public long jenrl(Variable variable, Value value) {
        Lecture anotherLecture = (Lecture)(first().equals(variable)?second():first());
        if (anotherLecture.getAssignment()==null) return 0;
        Lecture lecture = (Lecture) variable;
        return (isInConflict((Placement)anotherLecture.getAssignment(),(Placement)value)?iJenrl:0);
    }

    /** True if the given two lectures overlap in time */
    public boolean isInConflict() {
        return iAdded;
    }

    /** True if the given two lectures overlap in time */
    public boolean isInConflictPrecise() {
        return isInConflict((Placement)first().getAssignment(),(Placement)second().getAssignment());
    }

    /** Increment the number of joined enrollments (during student final sectioning) */
    public void incJenrl() {
        if (iJenrl==0) {
            ((Lecture)first()).addActiveJenrl(this);
            ((Lecture)second()).addActiveJenrl(this);
        }
        iJenrl++;
        if (iAdded) iGlobalViolations.inc(1);
    }
    
    /** Decrement the number of joined enrollments (during student final sectioning) */
    public void decJenrl() {
        iJenrl--;
        if (iAdded) iGlobalViolations.dec(1);
        if (iJenrl==0) {
            ((Lecture)first()).removeActiveJenrl(this);
            ((Lecture)second()).removeActiveJenrl(this);
        }
    }

    /** Number of joined enrollments (during student final sectioning) */
    public long getJenrl() { return iJenrl; }
    public boolean isHard() { return false; }
    
    public String toString() {
        return "JENR{"+first().getName()+","+second().getName()+","+(iAdded?iJenrl:0)+"/"+iJenrl+"}";
    }
       
}
