package ttsolver.model;


import ifs.model.*;
import ifs.util.*;

import java.util.*;

import ttsolver.*;
import ttsolver.constraint.*;

/**
 * Lecture (variable).
 *
 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomas Muller</a>
 * @version 1.0
 */

public class Lecture extends Variable {
    private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Lecture.class);
    private long iClassId;
    private String iName;
    private String iDept;
    private Vector iTimeLocations;
    private Vector iRoomLocations;
    private String iInstructorId;
    private String iInstructorName;
    private int iBestRoomPref;
    private double iBestTimePref;    
    private long iNrStudents = -1;
    private int iExpectedCapacity;
    private Hashtable iSameRoomValues = null;
    private String iPattern = null;
    
    private Vector iStudents = new FastVector(1);
    private Vector iAllStudents = null;
    private Vector iSameLectures = null;
    private Vector iCrossListedLectures = null;
    private DepartmentSpreadConstraint iDeptSpreadConstrain = null;
    private InstructorConstraint iInstructorConstraint = null;

    //private Vector iDoneSwaps = new FastVector(1);
    
    private Hashtable iSameStudents = new Hashtable(10);
    private Vector iActiveJenrls = new FastVector(10);
    private Vector iJenrlConstraints = new FastVector(10);
    
    private static boolean sSaveMemory = false;
        
    /** Constructor
     * @param id unique identification
     * @param name class name
     * @param timeLocations set of time locations
     * @param bestTimePref best time preference over all time locations
     * @param instructorId instuctor id
     * @param roomLocations set of room location
     * @param bestRoomPref best room preference over all room locations
     * @param initialPlacement initial placement
     * @param expectedCapacity expected number of students
     * @param inctructorName instructor name
     * @param pattern time pattern (e.g., 2x100)
     */
    public Lecture(long id, String name, Vector timeLocations, double bestTimePref, String instructorId, Vector roomLocations, int bestRoomPref, Placement initialPlacement, int expectedCapacity, String inctructorName, String pattern) {
        super(initialPlacement);
        iClassId = id;
        iTimeLocations = timeLocations;
        iInstructorId = instructorId;
        iInstructorName = inctructorName;
        iRoomLocations = roomLocations;
        iName = name;
        iBestRoomPref = bestRoomPref;
        iBestTimePref = bestTimePref;
        iExpectedCapacity = expectedCapacity;
        iPattern = pattern;
        if (!sSaveMemory)
            setValues(computeValues());
    }
    
    /** Add active jenrl constraint (active mean that there is at least one student between its classes) */
    public void addActiveJenrl(JenrlConstraint constr) { iActiveJenrls.addElement(constr); }
    /** Active jenrl constraints (active mean that there is at least one student between its classes) */
    public Vector activeJenrls() { return iActiveJenrls; }
    /** Remove active jenrl constraint (active mean that there is at least one student between its classes) */
    public void removeActiveJenrl(JenrlConstraint constr) { iActiveJenrls.removeElement(constr); }
    
    /** Class id */
    public long getClassId() { return iClassId; }
    /** Class name */
    public String getName() { return iName; }
    /** Class id */
    public long getId() { return iClassId; }
    /** Instructor name */
    public String getInstructorName() { return iInstructorName; }
    /** Instructor id */
    public String getInstructorId() { return iInstructorId; }
    /** List of enrolled students */
    public Vector students() { return iStudents; }
    /** List of enrolled students of this class and of all its cross-listed classes */
    public Vector allStudents() { return (iAllStudents==null?iStudents:iAllStudents); }
    /** Add an enrolled student */
    public void addStudent(String student) {
        iStudents.addElement(student);
        if (iAllStudents!=null) iAllStudents.addElement(student);
    }
    /** Add a student enrolled to a cross-listed class */
    public void addSameLectureStudent(String student) {
        iAllStudents.addElement(student);
    }
    /** Returns true if the given student is enrolled */
    public boolean hasStudent(String student) { return (iAllStudents==null?iStudents:iAllStudents).contains(student);}
    /** Set of lectures of the same class (only section is different) */
    public void setSameLectures(Vector sameLectures) {  iSameLectures = sameLectures; }
    /** Set of lectures of the same class (only section is different) */
    public Vector sameLectures() { return iSameLectures; }
    /* Set of cross-listed classes */
    public Vector crossListedLectures() { return iCrossListedLectures; }
    /* Set of cross-listed classes */
    public void setCrossListedLectures(Vector crossListedLectures) {
        iCrossListedLectures = crossListedLectures;
        if (iCrossListedLectures!=null) {
            iAllStudents = new FastVector(1);
            for (Enumeration e2=iCrossListedLectures.elements();e2.hasMoreElements();)
                ((CrossListedLecture)e2.nextElement()).setLecture(this);
        }
    }
    /** List of students enrolled in this class as well as in the given class */
    public Vector sameStudents(Lecture lecture) {
        if (iSameStudents.containsKey(lecture)) return (Vector)iSameStudents.get(lecture);
        Vector ret = new FastVector();
        for (Enumeration e=allStudents().elements(); e.hasMoreElements();) {
            String student = (String)e.nextElement();
            if (lecture.allStudents().contains(student)) ret.addElement(student);
        }
        iSameStudents.put(lecture, ret);
        return ret;
    }
    /** List of students which are in conflict with the given two classes */
    public Vector conflictStudents(Value value, Variable anotherVariable) {
        Vector ret = new FastVector();
        if (value==null) return ret;
        Lecture lecture = (Lecture)anotherVariable;
        if (lecture.getAssignment()==null || lecture.equals(this)) return ret;
        if (!JenrlConstraint.isInConflict((Placement)value,(Placement)lecture.getAssignment())) return ret;
        return sameStudents(lecture);
    }
    /** List of students of this class in conflict with the given assignment */
    public Vector conflictStudents(Value value) {
        Vector ret = new FastVector();
        if (value==null) return ret;
        if (getAssignment()!=null && value.equals(getAssignment())) {
            for (Enumeration i1=activeJenrls().elements(); i1.hasMoreElements();) {
                JenrlConstraint jenrl = (JenrlConstraint) i1.nextElement();
                ToolBox.merge(ret, sameStudents((Lecture)jenrl.another(this)));
            }
        } else {
            for (Enumeration i1=iJenrlConstraints.elements(); i1.hasMoreElements();) { //constraints()
                JenrlConstraint jenrl = (JenrlConstraint)i1.nextElement();// constraint;
                if (jenrl.jenrl(this, value)>0)
                    ToolBox.merge(ret, sameStudents((Lecture)jenrl.another(this)));
            }
        }
        return ret;
    }

    /** List of students of this class which are in conflict with any other assignment */
    public Vector conflictStudents() {
        Vector ret = new FastVector();
        if (getAssignment()==null) return ret;
        for (Enumeration i1=activeJenrls().elements(); i1.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint) i1.nextElement();
            ToolBox.merge(ret, sameStudents((Lecture)jenrl.another(this)));
        }
        return ret;
    }
    
    /** Lectures which share the given student, different from this one */
    public Vector sameStudentLectures(String student) {
        Vector ret = new FastVector();
        for (Enumeration it=iJenrlConstraints.elements();it.hasMoreElements();) {//constraints()
            JenrlConstraint jenrl = (JenrlConstraint)it.nextElement();
            Lecture lect = (Lecture)jenrl.another(this);
            if (!lect.equals(this) && lect.allStudents().contains(student)) ret.addElement(lect);
        }
        return ret;
    }
    /** Names of lectures different from this one, where it is student conflict of the given student between this and the lecture */
    public Vector conflictLecturesNames(String student) {
        Vector ret = new FastVector();
        if (getAssignment()==null) return ret;
        for (Enumeration it=activeJenrls().elements();it.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint) it.nextElement();
            Lecture lect = (Lecture)jenrl.another(this);
            if (!lect.equals(this) && lect.allStudents().contains(student)) ret.addElement(lect.getName());
        }
        return ret;
    }
    /** Lectures different from this one, where it is student conflict of the given student between this and the lecture */
    public Vector conflictLectures(String student) {
        Vector ret = new FastVector();
        if (getAssignment()==null) return ret;
        for (Enumeration it=activeJenrls().elements();it.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint) it.nextElement();
            Lecture lect = (Lecture)jenrl.another(this);
            if (lect.allStudents().contains(student)) ret.addElement(lect);
        }
        return ret;
    }
    /** Lectures different from this one, which are in a student conflict with the given assignment (of this lecture)*/
    public Vector conflictLectures(Value value) {
        Vector ret = new FastVector();
        for (Enumeration it=iJenrlConstraints.elements();it.hasMoreElements();) {//constraints()
            JenrlConstraint jenrl = (JenrlConstraint)it.nextElement();
            Lecture lect = (Lecture)jenrl.another(this);
            if (jenrl.jenrl(this, value)>0) ret.addElement(lect);
        }
        return ret;
    }
    /** Lectures different from this one, which are in a student conflict with the this lecture*/
    public Vector conflictLectures() {
        Vector ret = new FastVector();
        for (Enumeration it=activeJenrls().elements();it.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint) it.nextElement();
            Lecture lect = (Lecture)jenrl.another(this);
            ret.addElement(lect.getName());
        }
        return ret;
        
    }
    /** True if this lecture is in a student conflict with the given student */
    public int isInConflict(String student) {
        if (getAssignment()==null) return 0;
        int ret = 0;
        for (Enumeration it=activeJenrls().elements();it.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint) it.nextElement();
            Lecture lect = (Lecture)jenrl.another(this);
            if (lect.allStudents().contains(student)) ret++;
        }
        return ret;
    }
    /** Swap students between this and the same lectures (lectures which differ only in the section) */
    public Set swapStudents() {
        if (iSameLectures==null || iStudents==null) return null;
        if (getAssignment()==null) return null;
        int totalConf = countStudentConflicts(getAssignment());
        Vector conflictStudents = conflictStudents();
        //sLogger.debug("  conflicts:"+totalConf+" "+conflictStudents);
        if (totalConf==0) return null;
        HashSet ret = new HashSet();
        //sLogger.debug("Solution before swap is "+getModel().getInfo()+".");
        if (iSameLectures.size()>1) for (Enumeration i1=conflictStudents.elements(); i1.hasMoreElements();) {
            String student = (String)i1.nextElement();
            int iniDelta=isInConflict(student);
            if (iniDelta==0) continue;
            int bestDelta=-1;
            //sLogger.debug("student:"+student+", confs:"+iniDelta+" "+conflictLecturesNames(student));
            Vector bestLecture=null;
            Vector bestStudent=null;
            for (Enumeration i2=iSameLectures.elements();i2.hasMoreElements();) {
                Lecture sameLecture = (Lecture)i2.nextElement();
                if (sameLecture.equals(this)) continue;
                //if (sameLecture.isInConflict(student)<bestDelta) {
                for (Enumeration i3=sameLecture.allStudents().elements();i3.hasMoreElements();) {
                    String anotherStudent = (String)i3.nextElement();
                    if (isSwapTabu(student,anotherStudent)) break;
                    if (bestDelta<0 && bestLecture!=null && bestLecture.size()>10) break;
                    int delta = isInConflict(anotherStudent)+sameLecture.isInConflict(student)-sameLecture.isInConflict(anotherStudent)-iniDelta;
                    if (delta<bestDelta) {
                        if (bestLecture==null) {
                            bestLecture=new FastVector(); bestStudent=new FastVector();
                        } else {
                            bestLecture.clear(); bestStudent.clear();
                        }
                        bestLecture.addElement(sameLecture);
                        bestStudent.addElement(anotherStudent);
                        bestDelta=delta;
                    } else if (delta==bestDelta) {
                        if (bestLecture==null) {
                            bestLecture=new FastVector(); bestStudent=new FastVector();
                        }
                        bestLecture.addElement(sameLecture);
                        bestStudent.addElement(anotherStudent);
                    } else continue;
                    //sLogger.debug("possible swap with "+anotherStudent+", confs:"+isInConflict(anotherStudent)+"+"+sameLecture.isInConflict(student)+"-"+sameLecture.isInConflict(anotherStudent)+"-"+iniDelta+", best:"+bestDelta);
                }
                if (bestDelta==0 && bestLecture!=null && bestLecture.size()>10) break;
                //}
            }
            if (bestLecture!=null) {
                int x = ToolBox.random(bestLecture.size());
                //sLogger.debug("Swap "+getName()+" "+student+" <-> "+((Lecture)bestLecture.elementAt(x)).getName()+" "+(String)bestStudent.elementAt(x)+", delta:"+bestDelta);
                //sLogger.debug(" -"+getName()+" "+student+": "+conflictLecturesNames(student));
                //sLogger.debug(" +"+getName()+" "+(String)bestStudent.elementAt(x)+": "+conflictLecturesNames((String)bestStudent.elementAt(x)));
                //sLogger.debug(" -"+((Lecture)bestLecture.elementAt(x)).getName()+" "+(String)bestStudent.elementAt(x)+": "+((Lecture)bestLecture.elementAt(x)).conflictLecturesNames((String)bestStudent.elementAt(x)));
                //sLogger.debug(" +"+((Lecture)bestLecture.elementAt(x)).getName()+" "+student+": "+((Lecture)bestLecture.elementAt(x)).conflictLecturesNames(student));
                swap(student, (Lecture)bestLecture.elementAt(x), (String)bestStudent.elementAt(x), bestDelta);
                ret.add(bestLecture.elementAt(x));
                //sLogger.debug("Solution after swap is "+getModel().getInfo()+".");
            } else {
                //sLogger.debug("no swap found");
            }
        } else for (Enumeration i1=conflictStudents.elements(); i1.hasMoreElements();) { //no same lecture
            String student = (String)i1.nextElement();
            int iniDelta=isInConflict(student);
            if (iniDelta==0) continue;
            int bestDelta=iniDelta;
            //sLogger.debug("student:"+student+", confs:"+bestDelta+" "+conflictLecturesNames(student));
            for (Enumeration i2=conflictLectures(student).elements(); i2.hasMoreElements();) {
                Lecture anotherLecture = (Lecture)i2.nextElement();
                if (!anotherLecture.equals(this) && anotherLecture.getAssignment()!=null && anotherLecture.sameLectures().size()>1) {
                    Set lects = anotherLecture.swapStudents();
                    if (lects!=null) ret.addAll(lects);
                }
            }
        }
        return ret;
    }
    /** Prohibited swaps */
    public boolean isSwapTabu(String student, String swapStudent) {
        return false;
        //return iDoneSwaps.contains(student+","+swapStudent);
    }
    /** Swap two students between this and swapLecture*/
    private void justSwap(String student, Lecture swapLecture, String swapStudent) {
        Vector source = null, target = null;
        if (iAllStudents==null || iStudents.contains(student)) source = iStudents;
        else {
            for (Enumeration e=iCrossListedLectures.elements();source==null && e.hasMoreElements();) {
                CrossListedLecture sl = (CrossListedLecture)e.nextElement();
                if (sl.students().contains(student)) source=sl.students();
            }
        }
        if (swapLecture.iAllStudents==null || swapLecture.iStudents.contains(swapStudent)) target = swapLecture.iStudents;
        else {
            for (Enumeration e=swapLecture.iCrossListedLectures.elements();target==null && e.hasMoreElements();) {
                CrossListedLecture sl = (CrossListedLecture)e.nextElement();
                if (sl.students().contains(swapStudent)) target=sl.students();
            }
        }
        target.setElementAt(student, target.indexOf(swapStudent));
        source.setElementAt(swapStudent, source.indexOf(student));
        if (iAllStudents!=null)
            iAllStudents.setElementAt(swapStudent, iAllStudents.indexOf(student));
        if (swapLecture.iAllStudents!=null)
            swapLecture.iAllStudents.setElementAt(student, swapLecture.iAllStudents.indexOf(swapStudent));
        //swapLecture.iStudents.setElementAt(student, swapLecture.iStudents.indexOf(swapStudent));
        //iStudents.setElementAt(swapStudent, iStudents.indexOf(student));
    }
    /** Swap two students between this and swapLecture*/
    private void swap(String student, Lecture swapLecture, String swapStudent, int delta) {
        //iDoneSwaps.addElement(student+","+swapStudent);
        Vector jenrlsToRemove = new FastVector();
        for (Enumeration it=iJenrlConstraints.elements();it.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint)it.nextElement();
            for (Enumeration it2=jenrl.variables().elements();it2.hasMoreElements();) {
                Lecture anotherLecture = (Lecture)it2.nextElement();
                if (anotherLecture.equals(this)) continue;
                if (anotherLecture.allStudents().contains(student)) {
                    //sLogger.debug(getName()+": jenr-- {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                    jenrl.decJenrl();
                    if (jenrl.getJenrl()==0) {
                        jenrlsToRemove.addElement(jenrl);
                        //sLogger.debug(getName()+": remove jenr {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                    }
                }
            }
        }
        for (Enumeration it=swapLecture.iJenrlConstraints.elements();it.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint)it.nextElement();
            for (Enumeration it2=jenrl.variables().elements();it2.hasMoreElements();) {
                Lecture anotherLecture = (Lecture)it2.nextElement();
                if (anotherLecture.equals(swapLecture)) continue;
                if (anotherLecture.allStudents().contains(swapStudent)) {
                    //sLogger.debug(swapLecture.getName()+": jenr-- {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                    jenrl.decJenrl();
                    if (jenrl.getJenrl()==0) {
                        jenrlsToRemove.addElement(jenrl);
                        //sLogger.debug(swapLecture.getName()+": remove jenr {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                    }
                }
            }
        }
        if (!jenrlsToRemove.isEmpty()) {
            Object[] delete = jenrlsToRemove.toArray();
            for (int i=0;i<delete.length;i++) {
                Constraint c = (Constraint)delete[i];
                Object[] vars = c.variables().toArray();
                for (int j=0;j<vars.length;j++)
                    c.removeVariable((Variable)vars[j]);
                getModel().removeConstraint(c);
            }
        }
        justSwap(student, swapLecture, swapStudent);
        swapLecture.iSameStudents.clear();
        iSameStudents.clear();
        Vector jenrlsToAdd = new FastVector();
        for (Enumeration it=getModel().variables().elements(); it.hasMoreElements();) {
            Lecture anotherLecture = (Lecture)it.nextElement();
            if (anotherLecture.equals(this) || anotherLecture.equals(swapLecture)) continue;
            if (anotherLecture.allStudents().contains(swapStudent)) {
                boolean found = false;
                for (Enumeration itc=iJenrlConstraints.elements();!found && itc.hasMoreElements();) {
                    JenrlConstraint jenrl = (JenrlConstraint)itc.nextElement();
                    for (Enumeration it2=jenrl.variables().elements();it2.hasMoreElements();) {
                        Lecture xLecture = (Lecture)it2.nextElement();
                        if (xLecture.equals(anotherLecture)) {
                            found = true;
                            //sLogger.debug(getName()+": jenr++ {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                            jenrl.incJenrl();
                        }
                    }
                }
                if (!found) {
                    JenrlConstraint jenrl = new JenrlConstraint(1, ((TimetableModel)getModel()).getViolatedStudentConflictsCounter());
                    jenrl.addVariable(this);
                    jenrl.addVariable(anotherLecture);
                    //sLogger.debug(getName()+": add jenr {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                    jenrlsToAdd.addElement(jenrl);
                }
            }
            if (anotherLecture.allStudents().contains(student)) {
                boolean found = false;
                for (Enumeration itc=swapLecture.iJenrlConstraints.elements();!found && itc.hasMoreElements();) {
                    JenrlConstraint jenrl = (JenrlConstraint)itc.nextElement();
                    for (Enumeration it2=jenrl.variables().elements();it2.hasMoreElements();) {
                        Lecture xLecture = (Lecture)it2.nextElement();
                        if (xLecture.equals(anotherLecture)) {
                            found = true;
                            //sLogger.debug(swapLecture.getName()+": jenr++ {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                            jenrl.incJenrl();
                        }
                    }
                }
                if (!found) {
                    JenrlConstraint jenrl = new JenrlConstraint(1, ((TimetableModel)getModel()).getViolatedStudentConflictsCounter());
                    jenrl.addVariable(swapLecture);
                    jenrl.addVariable(anotherLecture);
                    //sLogger.debug(swapLecture.getName()+": add jenr {conf="+jenrl.isInConflict()+", lect="+anotherLecture.getName()+", jenr="+jenrl+"}");
                    jenrlsToAdd.addElement(jenrl);
                }
            }
        }
        if (!jenrlsToAdd.isEmpty()) {
            Object[] add = jenrlsToAdd.toArray();
            for (int i=0;i<add.length;i++)
                getModel().addConstraint((Constraint)add[i]);
        }
    }
    
    /** Domain -- all combinations of room and time locations */
    public Vector computeValues() {
        Vector values = new FastVector();
        iSameRoomValues = new Hashtable();
        for (Enumeration i2=iRoomLocations.elements();i2.hasMoreElements();) {
            RoomLocation roomLocation = (RoomLocation)i2.nextElement();
            Vector placements = new FastVector();
            for (Enumeration i1=iTimeLocations.elements();i1.hasMoreElements();) {
                TimeLocation timeLocation = (TimeLocation)i1.nextElement();
                Placement p = new Placement(this,timeLocation, roomLocation);
                p.setVariable(this);
                if (getInitialAssignment()!=null && p.equals(getInitialAssignment())) {
                    setInitialAssignment(p);
                    p.setInitial(true);
                }
                if (getAssignment()!=null && getAssignment().equals(p)) iValue=getAssignment();
                if (getBestAssignment()!=null && getBestAssignment().equals(p)) setBestAssignment(p);
                values.addElement(p);
                placements.addElement(p);
            }
            iSameRoomValues.put(roomLocation.getId(),placements);
        }
        return values;
    }
    
    /** All values which uses the given room */
    public Vector getSameRoomValues(String roomId) {
        if (sSaveMemory) {
            for (Enumeration i2=iRoomLocations.elements();i2.hasMoreElements();) {
                RoomLocation roomLocation = (RoomLocation)i2.nextElement();
                if (!roomLocation.getId().equals(roomId)) continue;
                Vector placements = new FastVector();
                for (Enumeration i1=iTimeLocations.elements();i1.hasMoreElements();) {
                    TimeLocation timeLocation = (TimeLocation)i1.nextElement();
                    Placement p = new Placement(this,timeLocation, roomLocation);
                    p.setVariable(this);
                    if (getInitialAssignment()!=null && p.equals(getInitialAssignment())) {
                        setInitialAssignment(p);
                        p.setInitial(true);
                    }
                    if (getAssignment()!=null && getAssignment().equals(p)) iValue=getAssignment();
                    if (getBestAssignment()!=null && getBestAssignment().equals(p)) setBestAssignment(p);
                    placements.addElement(p);
                }
                return placements;
            }
        }
        return (Vector)iSameRoomValues.get(roomId);
    }
    
    /** All values */
    public Vector values() {
        if (sSaveMemory) {
            Vector values = new FastVector(iRoomLocations.size()*iTimeLocations.size());
            for (Enumeration i2=iRoomLocations.elements();i2.hasMoreElements();) {
                RoomLocation roomLocation = (RoomLocation)i2.nextElement();
                Vector placements = new FastVector();
                for (Enumeration i1=iTimeLocations.elements();i1.hasMoreElements();) {
                    TimeLocation timeLocation = (TimeLocation)i1.nextElement();
                    Placement p = new Placement(this,timeLocation, roomLocation);
                    p.setVariable(this);
                    if (getInitialAssignment()!=null && p.equals(getInitialAssignment())) {
                        setInitialAssignment(p);
                        p.setInitial(true);
                    }
                    if (getAssignment()!=null && getAssignment().equals(p)) iValue=getAssignment();
                    if (getBestAssignment()!=null && getBestAssignment().equals(p)) setBestAssignment(p);
                    values.addElement(p);
                }
            }
            return values;            
        } else
            return super.values();
    }
    
    public boolean equals(Object o) {
        try {
            return getId()==((Lecture)o).getId();
        } catch (Exception e) {
            return false;
        }
    }
    
    /** Best time preference of this lecture */
    public double getBestTimePreference() { return iBestTimePref; }
    /** Best room preference of this lecture */
    public int getBestRoomPreference() { return iBestRoomPref; }
    
    /** Number of student conflicts caused by the given assignment of this lecture */
    public int countStudentConflicts(Value value) {
        int studentConflictsSum = 0;
        for (Enumeration i=jenrlConstraints().elements();i.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
            studentConflictsSum += jenrl.jenrl(this, value);
        }
        return studentConflictsSum;
    }

    /** Number of student conflicts caused by the initial assignment of this lecture */
    public int countInitialStudentConflicts() {
        Value value = getInitialAssignment();
        if (value==null) return 0;
        int studentConflictsSum = 0;
        for (Enumeration i=jenrlConstraints().elements();i.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
            Lecture another = (Lecture)jenrl.another(this);
            if (another.getInitialAssignment()!=null)
                if (JenrlConstraint.isInConflict((Placement)value,(Placement)another.getInitialAssignment()))
                    studentConflictsSum += jenrl.getJenrl();
        }
        return studentConflictsSum;
    }

    /** Table of student conflicts caused by the initial assignment of this lecture in format (another lecture, number)*/
    public Hashtable getInitialStudentConflicts() {
        Value value = getInitialAssignment();
        if (value==null) return null;
        Hashtable ret = new Hashtable();
        for (Enumeration i=jenrlConstraints().elements();i.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
            Lecture another = (Lecture)jenrl.another(this);
            if (another.getInitialAssignment()!=null)
                if (JenrlConstraint.isInConflict((Placement)value,(Placement)another.getInitialAssignment()))
                    ret.put(another,new Long(jenrl.getJenrl()));
        }
        return ret;
    }

    /** List of student conflicts caused by the initial assignment of this lecture */
    public Set initialStudentConflicts() {
        Value value = getInitialAssignment();
        if (value==null) return null;
        HashSet ret = new HashSet();
        for (Enumeration i=jenrlConstraints().elements();i.hasMoreElements();) {
            JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
            Lecture another = (Lecture)jenrl.another(this);
            if (another.getInitialAssignment()!=null)
                if (JenrlConstraint.isInConflict((Placement)value,(Placement)another.getInitialAssignment()))
                    ret.addAll(sameStudents(another));
        }
        return ret;
    }
    
    public void addContstraint(Constraint constraint) {
        super.addContstraint(constraint);
        if (constraint instanceof JenrlConstraint)
            iJenrlConstraints.addElement(constraint);
        else if (constraint instanceof DepartmentSpreadConstraint)
            iDeptSpreadConstrain = (DepartmentSpreadConstraint)constraint;
        else if (constraint instanceof InstructorConstraint)
            iInstructorConstraint = (InstructorConstraint)constraint;
    }
    public void removeContstraint(Constraint constraint) {
        super.removeContstraint(constraint);
        if (iJenrlConstraints.contains(constraint))
            iJenrlConstraints.removeElement(constraint);
    }
    
    /** All JENRL constraints of this lecture */
    public Vector jenrlConstraints() {
        return iJenrlConstraints;
    }
    
    /** Expected capacity */
    public long countStudents() {
        return iExpectedCapacity;
        /*if (iNrStudents>=0) return iNrStudents;
        long ret = 0;
        for (Enumeration i=jenrlConstraints().elements();i.hasMoreElements();) {
            ret += ((JenrlConstraint)i.nextElement()).getJenrl();
        }
        iNrStudents = ret;
        ToolBox.print("Class "+getName()+" has "+ret+(students()==null?"":" ("+students().size()+")")+" students.");
        return ret;*/
    }
    
    public String toString() {
        return getName()+" ["+getDescription()+"]";//"Lecture{name='"+getName()+"', time="+(getAssignment()==null?"?":String.valueOf(((Placement)getAssignment()).getTimeLocation().getNormalizedPreference()))+"/"+iBestTimePref+", room="+(getAssignment()==null?"?":String.valueOf(((Placement)getAssignment()).getRoomLocation().getPreference()))+"/"+iBestRoomPref+", super="+super.toString()+"}";
    }
    
    /** Description */
    public String getDescription() {
        return (iPattern==null?"":iPattern+", ")+iExpectedCapacity+" exp. students"+
               (iBestTimePref!=0?", "+iBestTimePref+" best time pref.":"")+
               (iBestRoomPref!=0?", "+iBestRoomPref+" best room pref.":"")+
               (getInitialAssignment()==null?"":", initial "+getInitialAssignment().getName())+
               ", "+(values().size()==1?"value "+((Placement)values().firstElement()).getName()+" required":values().size()<=5?"values=["+getValuesString()+"]":values().size()+" values");
    }
    
    public String getValuesString() {
        StringBuffer sb = new StringBuffer();
        for (Enumeration e=values().elements();e.hasMoreElements();) {
            Placement p = (Placement)e.nextElement();
            sb.append(p.getName()).append(e.hasMoreElements()?", ":"");
        }
        return sb.toString();
    }

    /** Department */
    public String getDepartment() { return iDept;}
    /** Department */
    public void setDepartment(String dept) { iDept=dept; }
    /** Departmental spreading constraint */
    public DepartmentSpreadConstraint getDeptSpreadConstraint() { return iDeptSpreadConstrain; }
    /** Instructor constraint */
    public InstructorConstraint getInstructorConstraint() { return iInstructorConstraint; }    
    /** All room locations */
    public Vector roomLocations() { return iRoomLocations; }
    /** All time locations */
    public Vector timeLocations() { return iTimeLocations; }
}