/* REVISED Monday 31 January to fix a bug in ValidBooking. 
*/

/* File apptcal. The ApptCalendar ADT implemented using a linked list. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <math.h>
#include "bool.h"
#include "apptcal.h"
#include "appt.h"
#include "time.h"


char *DayStrings[5] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};



/* ApptCalInit()
 *----------------------------------------------------------------
 * Initialize the appointment calendar
 */
void
ApptCalInit(Calendar *C)
{
    int day;
    ClockTime EightFiftyNine = MakeTime(8, 59);
    ClockTime SeventeenOclock = MakeTime(17, 0);
    ApptNode *earlyMorning, *lateEvening;
    
    for(day =  Monday; day <=  Friday; day++) {
	lateEvening = (ApptNode *)malloc(sizeof(ApptNode));
	SetAppt(&(lateEvening->appt), SeventeenOclock, MakeTime(0,0),
		"TooLateForMe", TRUE);
	lateEvening->next = NULL;

	earlyMorning = (ApptNode *)malloc(sizeof(ApptNode));
	SetAppt(&(earlyMorning->appt), MakeTime(0,0), EightFiftyNine,
		"TooEarlyForMe", TRUE);
	earlyMorning->next = lateEvening;
	C->WorkWeek[day] = earlyMorning;
    }
    C->currentDay = Monday;
    strncpy(C->fileName, "apptcal.dat", sizeof("apptcal.dat") +1);
}

/* DisplayDay
 *----------------------------------------------------------------
 */
void
DisplayDay(Workday day)
{
    printf("%s", DayStrings[day]);
}

Workday
MakeDayFromString(char *dayStr)
{
    Workday i;
    for(i = Monday; i <=Friday; i++)
	if(strncmp(DayStrings[i], dayStr, sizeof(dayStr)) == 0)
	    return i;
    /* Did not find a valid day */
    return -1;
}

/* ConvertTimeToIndex
 *----------------------------------------------------------------
 * Return the index into DayAppts that corresponds to time.
 */
int
ConvertTimeToIndex (ClockTime time)
{
    int hour = GetHour (time);
    int minute = GetMinute (time);

    assert( hour >= 9 && hour - 9 <= MAX_APPTS/2);
    assert( 0 <= minute && minute <= 59);
    
    return ((hour - 9) * 60 + minute) / 30; /*note: integer division*/    
}


/* ConvertIndexToTime
 *----------------------------------------------------------------
 * Return the time corresponding to index.
 */
ClockTime
ConvertIndexToTime(int index)
{
    ClockTime t;
    int hour = 9 + index / 2;
    int minute = 30 * (index % 2);
    
    assert( hour >= 9 && hour - 9 <= MAX_APPTS/2);
    assert( 0 <= minute && minute <= 59);

    t = MakeTime(hour, minute);
    return t;
}


/* ValidBooking
 *----------------------------------------------------------------
 * Return true iff the appointment slot on day between startTime
 * and endTime is not used by other appointments.
 * REVISED version.  Doesn't mess up when the end time of one
 * appointment = the start time of the next. 
*/
boolean
ValidBooking(Calendar *C, Workday day, ClockTime startTime, ClockTime endTime)
{
    /* We're guaranteed to have both of these because of the virtual
       appointments. */
    ApptNode *currAppt = C->WorkWeek[day];
    ApptNode *nextAppt = currAppt->next;

    /* Postcondition : nextAppt points to an appointment which starts
       later than startTime.*/
    while((!TimeGreaterThan(nextAppt->appt.startTime, endTime)) &&
          !TimeEqual(nextAppt->appt.startTime, endTime))
    {
        currAppt = nextAppt;
        nextAppt = nextAppt->next;
    }

    /* Return true if the new appointment fits.*/
    return ((TimeGreaterThan(startTime, currAppt->appt.endTime) ||
             TimeEqual(startTime, currAppt->appt.endTime)) &&
            (TimeGreaterThan(nextAppt->appt.startTime, endTime) ||
             TimeEqual(nextAppt->appt.startTime, endTime)));
}



/* CalendarBook
 *----------------------------------------------------------------
 */
boolean
CalendarBook(Calendar *C, Workday day, ClockTime startTime, ClockTime endTime,
     char *message, boolean fixed)
{
    ApptNode *newAppt, *currAppt, *nextAppt;
    
    if(!ValidBooking(C, day, startTime, endTime))
	return FALSE;
    else { /* It will fit.*/
	/* Create a node, fill it with the necessary info, and
	   stick it in the list.*/

	newAppt = (ApptNode *)malloc(sizeof(ApptNode));
	SetAppt(&(newAppt->appt), startTime, endTime, message, fixed);
	
        /* Find out where it goes.*/
	currAppt = C->WorkWeek[day];
        nextAppt = currAppt->next;

	/* Postcondition : nextAppt points to an appointment which starts
	   later than startTime.*/
	while(!TimeGreaterThan(nextAppt->appt.startTime, startTime)){
	    currAppt = currAppt->next;
	    nextAppt = nextAppt->next;
	}
	    
	newAppt->next = nextAppt;
	currAppt->next = newAppt;
	return TRUE;
    }
}


/* PackAppts
 *----------------------------------------------------------------
 * Pack all appointments between appt1 and appt2 by
 * setting the start and end times as early as possible.  Set
 * appt1 to be the last appointment before appt2.
 */
void
PackAppts(ApptNode *appt1, ApptNode *appt2)
{
    ApptNode *prevAppt = appt1;
    ApptNode *currAppt = appt1->next;

    while(currAppt != appt2) {

	/* Now set the start and end times as early as possible.*/
	ClockTime duration = TimeDifference(currAppt->appt.startTime,
					    currAppt->appt.endTime);

	currAppt->appt.startTime = TimeAdd(prevAppt->appt.endTime,
					   MakeTime(0, 1));

	currAppt->appt.endTime = TimeAdd(currAppt->appt.startTime, duration);

	currAppt = currAppt->next;
	prevAppt = prevAppt->next;
    }
    
    appt1 = prevAppt;
}

/* CalendarFit
 *----------------------------------------------------------------
 */
boolean
CalendarFit(Calendar *C, Workday day, ClockTime totalTime, char *message,
    boolean fixed, ClockTime *startTime, ClockTime *endTime)
{
        /*  <<< YOU COMPLETE THIS SUBPROGRAM >>>  */
}


/* CalendarFind
 *----------------------------------------------------------------
 */
boolean
CalendarFind(Calendar *C, char *message, Workday *day,
     ClockTime *startTime, ClockTime *endTime)
{
    Workday dayCounter;
    ApptNode *currAppt;

    for(dayCounter = Monday; dayCounter <= Friday; dayCounter++) {
	/* Skip the first (virtual) appointment.*/
	currAppt = C->WorkWeek[dayCounter]->next;

	/* Search the current day.*/
	while(currAppt->next != NULL) {
	
	    if((strncmp(currAppt->appt.message, message,
			MAX_MESSAGE_LEN)) == 0) {
		*day = dayCounter;
		*startTime = currAppt->appt.startTime;
		*endTime = currAppt->appt.endTime;
		return TRUE;
	    }
	    currAppt = currAppt->next;
	}
    }
    return FALSE;
}


/* CalendarCancel
 *----------------------------------------------------------------
 */
void 
CalendarCancel(Calendar *C, char *message)
{
    Workday day;
    ClockTime startTime, endTime;
    boolean success;
    ApptNode *prevAppt, *currAppt;
    
    success = CalendarFind(C, message, &day, &startTime, &endTime);
    
    /* If we found it, cancel it.*/
    if(success) {
	prevAppt = C->WorkWeek[day];
	currAppt = C->WorkWeek[day]->next;
	
	/* Search the current day.*/
        while((strncmp(message, currAppt->appt.message, MAX_MESSAGE_LEN))!= 0){
	    prevAppt = prevAppt->next;
	    currAppt = currAppt->next;
	}
	
        /* We found it, so remove it from the list and free it. */
	prevAppt->next = currAppt->next;
	free(currAppt);
    }
}
 
/* CalendarClearDay
 *----------------------------------------------------------------
 */
void
CalendarClearDay(Calendar *C, Workday day)
{
    ApptNode *tempAppt;
    /* Remove the items between EarlyMornMsg and LateEveMsg.*/
    while(C->WorkWeek[day]->next->next != NULL) {
	tempAppt = C->WorkWeek[day]->next;
	C->WorkWeek[day]->next = C->WorkWeek[day]->next->next;
	free(tempAppt);
    }
}


/* CalendarClearWeek
 *-----------------------------------------------------------------
 */
void
CalendarClearWeek(Calendar *C)
{
    Workday day;

    for(day = Monday; day <= Friday; day++) {
	CalendarClearDay(C, day);
    }
}


/* CalendarViewDay
 *----------------------------------------------------------------
 */
void
CalendarViewDay(Calendar *C, Workday day)
{
        /*  <<< YOU COMPLETE THIS SUBPROGRAM >>>  */
}

/* CalendarViewToday
 *----------------------------------------------------------------
 */
void
CalendarViewToday(Calendar *C)
{
    CalendarViewDay(C, C->currentDay);
}



/* CalendarNextDay
 *----------------------------------------------------------------
 */
void
CalendarNextDay(Calendar *C)
{
        /*  <<< YOU COMPLETE THIS SUBPROGRAM >>>  */
}


/* CalendarViewWeek
 *----------------------------------------------------------------
 */
void
CalendarViewWeek(Calendar *C)
{
        /*  <<< YOU COMPLETE THIS SUBPROGRAM >>>  */
}


/* CalendarSave
 *----------------------------------------------------------------
 */
void
CalendarSave(Calendar *C)
{
    Workday day;
    ApptNode *currAppt;
    
    FILE *savefile = fopen(C->fileName, "w");
    assert(savefile != NULL);


    fprintf(savefile, "%s\n", DayStrings[C->currentDay]);
    
    for(day = Monday; day <= Friday; day++) {

	currAppt = C->WorkWeek[day]->next;
	while(currAppt->next != NULL) {
	    fprintf(savefile, "%s\n", DayStrings[day]);
	    SaveAppt(currAppt->appt, savefile);
	    currAppt = currAppt->next;
	}
    }
}

/* CalendarLoad
 *----------------------------------------------------------------
 */
void
CalendarLoad(Calendar *C)
{
    FILE *savefile = fopen(C->fileName, "r");
    Workday day;
    Appointment app;
    char curDayStr[10];
    int res;
    
    assert(savefile != NULL);
    
    if((res = ReadString(curDayStr, sizeof(curDayStr), savefile)) == EOF) {
	printf("Trying to load invalid Calendar file, exiting.\n");
	exit(-1);
    }
	

    C->currentDay = MakeDayFromString(curDayStr);
    
    if((res = ReadString(curDayStr, sizeof(curDayStr), savefile)) != EOF)
	res = LoadAppt(&app, savefile);

    day = MakeDayFromString(curDayStr);

    while(res != EOF) {
	CalendarBook(C, day, app.startTime, app.endTime,
		     app.message, app.fixed);

	/* read in the next appointment*/
	if((res = ReadString(curDayStr, sizeof(curDayStr), savefile)) != EOF){
	    res = LoadAppt(&app, savefile);
	    day  = MakeDayFromString(curDayStr);
	}
	
    }    
}

