package ttsolver.model;

import ifs.util.FastVector;
import java.util.*;
import edu.purdue.smas.timetable.data.pattern.TimePatternModel;
import edu.purdue.smas.timetable.util.Constants;

/**
 * Time part of placement.
 *
 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomas Muller</a>
 * @version 1.0
 */

public class TimeLocation {
    private int iStartSlot;
    private int[] iStartSlots;
    private int[] iSlots;
    
    private int iPreference;
    private double iNormalizedPreference;
    
    private TimePatternModel iTimePatternModel = null;
    private String iText=null;
    private int iHashCode;

    private int iDayCode;
    private int iTimeCode;
    private int iLength;
    
    /** Constructor
     * @param timePatternModel time pattern model
     * @param day day in time pattern model
     * @param time time in time pattern model
     */
    public TimeLocation(TimePatternModel timePatternModel, int day, int time) {
        iTimePatternModel = timePatternModel;
        iText = iTimePatternModel.getText(day, time);
        iLength = iTimePatternModel.getNrHalfHoursPerMeeting();
        iDayCode = iTimePatternModel.getDayCode(day);
        iNormalizedPreference = iTimePatternModel.getNormalizedPreference(day,time);
        Collection slots = iTimePatternModel.getSlots(day, time);
        iSlots = new int[slots.size()]; int i=0;
        for (Iterator it=slots.iterator();it.hasNext();) iSlots[i++]=((Integer)it.next()).intValue();
        Collection startSlots = iTimePatternModel.getStartSlots(day, time);
        iStartSlots = new int[startSlots.size()]; i=0;
        for (Iterator it=startSlots.iterator();it.hasNext();) iStartSlots[i++]=((Integer)it.next()).intValue();
        iStartSlot = iStartSlots[0] % Constants.SLOTS_PER_DAY;
        String prologPref = iTimePatternModel.getPreference(day, time).getProlog();
        iPreference = (prologPref.equalsIgnoreCase("R")?0:Integer.parseInt(iTimePatternModel.getPreference(day, time).getProlog()));
        iHashCode = combine(combine(day, time),combine(iTimePatternModel.getNrMeetings(),iTimePatternModel.getNrDays()));
        iTimeCode = 0;
        for (int h=0;h<iLength;h++)
            iTimeCode |= (1<<(iStartSlot+h));
    }
    
    /** Constructor
     * @param dayCode days (combination of 1 for Monday, 2 for Tuesday, ...)
     * @param startTime start slot
     * @param length number of slots
     * @param pref time preference
     */
    public TimeLocation(int dayCode, int startTime, int length, int pref) {
        iPreference = pref;
        iNormalizedPreference = pref;
        iStartSlot = startTime;
        iDayCode = dayCode;
        iLength = length;
        int nrDays = 0;
        for (int i=0;i<Constants.DAY_CODES.length;i++) {
            if ((iDayCode & Constants.DAY_CODES[i])==0) continue;
            nrDays++;
        }
        iSlots = new int[nrDays*length];
        iStartSlots = new int[nrDays];
        int idxStartSlots = 0, idxSlots = 0;
        for (int i=0;i<Constants.DAY_CODES.length;i++) {
            if ((iDayCode & Constants.DAY_CODES[i])==0) continue;
            int slot = (i*Constants.SLOTS_PER_DAY)+iStartSlot;
            iStartSlots[idxStartSlots++]=slot;
            for (int j=0;j<length;j++)
                iSlots[idxSlots++]=slot+j;
        }
        iHashCode = combine(combine(dayCode, iStartSlot),combine(iLength,nrDays));
        iTimeCode = 0;
        for (int h=0;h<iLength;h++)
            iTimeCode |= (1<<(iStartSlot+h));
        iText = getDayHeader()+" "+getStartTimeHeader();
    }
    
    /** Number of meetings */
    public int getNrMeetings() {
        return iStartSlots.length;
    }
    
    private static int combine(int a, int b) {
        int ret = 0;
        for (int i=0;i<15;i++) ret = ret | ((a & (1<<i))<<i) | ((b & (1<<i))<<(i+1));
        return ret;
    }
    
    /** Days (combination of 1 for Monday, 2 for Tuesday, ...) */
    public int getDayCode() { return iDayCode; }
    /** Days for printing purposes */
    public String getDayHeader() { 
        StringBuffer sb = new StringBuffer();
        for (int i=0;i<edu.purdue.smas.timetable.util.Constants.DAY_CODES.length;i++)
            if ((iDayCode & edu.purdue.smas.timetable.util.Constants.DAY_CODES[i])!=0)
                sb.append(edu.purdue.smas.timetable.util.Constants.DAY_NAMES_SHORT[i]);
        return sb.toString(); 
    }
    /** Start time for printing purposes */
    public String getStartTimeHeader() { 
        int h = (iStartSlot+15)/2;
        int m = ((iStartSlot+15)%2==0?0:30);
        return (h>12?h-12:h)+":"+(m<10?"0":"")+m+" "+(h>=12?"pm":"am");
    }
    /** End time for printing purposes */
    public String getEndTimeHeader() { 
        int h = (iStartSlot+15)/2;
        int m = ((iStartSlot+15)%2==0?0:30);
        int x = (h * 60) + m + (iLength*25);
        m = x % 60;
        h = x / 60;
        return (h>12?h-12:h)+":"+(m<10?"0":"")+m+" "+(h>=12?"pm":"am");
    }
    /** Start slot */
    public int getStartSlot() { return iStartSlot; }
    /** Used slots in a day (combination of 1..first, 2..second,...) */
    public int getTimeCode() { return iTimeCode; }
    
    /** true if days overlap */
    public boolean shareDays(TimeLocation anotherLocation) {
        return ((iDayCode & anotherLocation.iDayCode)!=0);
    }
    /** true if hours overlap */
    public boolean shareHours(TimeLocation anotherLocation) {
        return ((iTimeCode & anotherLocation.iTimeCode)!=0);
    }
    /** true if overlap */
    public boolean hasIntersection(TimeLocation anotherLocation) {
        return shareDays(anotherLocation) && shareHours(anotherLocation);
    }

    /** Set of overlapping slots */
    public Collection getIntersection(TimeLocation anotherLocation) { //called only in JenrlConstraint.toString() -- no caching needed
        Vector ret = new FastVector();
        for (int i=0;i<getSlots().length;i++)
            for (int j=0;j<anotherLocation.getSlots().length;j++)
                if (getSlots()[i]==anotherLocation.getSlots()[j]) ret.addElement(new Integer(getSlots()[i]));
        return ret;
    }

    /** Used slots */
    public int[] getSlots() { return iSlots; }
    /** Used start slots (for each meeting) */
    public int[] getStartSlots() { return iStartSlots; }
    /** Text representation */
    public String getText() { return iText; }
    /** Preference */
    public int getPreference() { return iPreference; }
    /** Length */
    public int getLength() { return iLength; }
    /** Length */
    public int getNrHalfHoursPerMeeting() { return iLength; }
    /** Normalized preference */
    public double getNormalizedPreference() { return iNormalizedPreference;}
    /** Time pattern model (can be null) */
    public TimePatternModel getModel() { return iTimePatternModel; }
    private static Vector toVector(int[] x) {
        if (x==null) return null;
        Vector ret = new FastVector(x.length);
        for (int i=0;i<x.length;i++) ret.add(new Integer(x[i]));
        return ret;
    }
    
    public String toString() { return "TimeLocation{text="+getText()+", pref="+iPreference+", normPref="+getNormalizedPreference()+(iTimePatternModel!=null?", tansform="+iTimePatternModel.getTransformPreference()+", treshold="+iTimePatternModel.getTresholdPreference():"")+", slots="+toVector(iSlots)+"}"; }
    public int hashCode() {
        return iHashCode;
    }
}
