package schedule;
import caldb.CalendarDB;
import caldb.Room;
import java.util.Collection;

/**
 * The Calendar object is derived from an overall view of Sections 2.1 through
 * 2.5 of the requirements.  The functionality described in those sections
 * makes it clear that a Calendar is the primary data object of the Calendar
 * Tool.
 *									     <p>
 * The data component of a Calendar is a collection of scheduled items.  The
 * operations are those that schedule each of the four types of scheduled
 * item.  In the case of meetings, there are two operations involved -- one to
 * compute a list of possible times, and another to confirm a specific selected
 * meeting time.
 */
public abstract class Calendar {

    Collection<ScheduledItem> data;

    /**
     * ScheduleAppointment adds the given Appointment to this.data, if an
     * appointment of the same time, duration, and title is not already
     * scheduled.
     */
    abstract void scheduleAppointment(Appointment appointment);

    /**
     * ScheduleMeeting uses the given MeetingRequest to determine possible
     * times that the requested meeting might be held, within the existing set
     * of scheduled items in the this.data.  The PossibleMeetingTimes output is
     * a list of zero or more possible times and dates that the meeting can be
     * held.
     *									  <pre> 
       pre:
	// There is at least one time that is between the given start time and
        // end time at which the room is available.
        exists (Time time ;
            inRange(time, starttime, endtime)
                &&
            roomAvailable(room, time)

       post:
        //
        // An item is properly added to the output db at the "best" time,
        // where best means the earliest possible time between the given start
        // time and end time at which the given room is available.
        //
        // Also, the room in which the item is scheduled is booked for the
        // item's time.
        //
        exists (ScheudledItem item; data'.contains(item) ;

            //
            // The item is scheduled at the best time.
            //
            scheduledAtBestTime(item, starttime, endtime, room)

                &&

            //
            // The item's room is booked for the item's time.
            //
            !roomAvailable(meetingRequest.room', item.time)

                &&

            //
            // The item info is that given in the input.
            //
            (item.info = info)

                and

            (*
             * Only the new item and original items are in the output item
             * db (this is standard logic for properly adding to a collection).
             *) 
            forall (item':ScheduledItem) (
                (item' in sidb') iff ((item' in sidb) or (item' = item))
            )
        );
     */
    abstract PossibleMeetingTimes scheduleMeeting(
        MeetingRequest meetingRequest);


    /**
     * A given meeting item is scheduled at the best time.
     *
      post:
       return ==

	//
	// The scheduled time is between the given start time and end time.
	//
	inRange(meeting.time, startTime, endTime)

	    &&

	//
	// The room is available at the scheduled time.
	//
	roomAvailable(room, meeting.time)

	    &&

	//
	// The scheduled time is the earliest possible.
	//
	isEarliest(meeting.time, startTime, endTime, room)
     */
    abstract boolean scheduledAtBestTime(Meeting meeting,
        Time startTime, Time endTime, Room room);

    /**
     * For a given time, no other in-range, non-booked time is earlier.
     *
      post:
       return ==
	!exists (otherTime:Time
		InRange(otherTime, startTime, endTime)
		    &&
		RoomAvailable(room, otherTime)
		    &&
		(otherTime.compareTo(time) < 0)
	    )
	)
     */
    abstract boolean isEarliest(Time time,
	Time startTime, Time endTime, Room room);

    /**
     * A given time is between a given start time and end time.
      post:
       return ==
	time.compareTo(startTime) >= 0 &&
	time.compareTo(endTime) <= 0;
     */
    abstract boolean InRange(Time time, Time startTime, Time endTime);

    /*
     * A given room is available at a given time.  Since the room object
     * contains a list of UNavailable times, the logic is negative.  I.e., a
     * room is available at time t means there is no time on the booked list
     * equal to t.
      post:
       return ==
	!exists (bookedTime; room.booked.contains(time); time == bookedTime);
     */
    abstract boolean roomAvailable(Room room, Time time);
    

    /**
     * ConfirmMeeting takes a MeetingRequest, list of PossibleMeetingTimes, and
     * a Selected time from the list.  It adds a meeting to this.data,
     * comprised of the given request, scheduled at the selected time.  Further
     * details of output constraints are forthcoming.
     */
    abstract void confirmMeeting(
        MeetingRequest request,
        PossibleMeetingTimes times,
        int selectedTime);

    /**
     * ScheduleTask adds the given Task to this.data, if a task of the same
     * time, duration, and title is not already scheduled.
     */
    abstract void scheduleTask(Task task);

    /**
     * ScheduleEvent adds the given Event to this.data, if an event of the same
     * time, duration, and title is not already scheduled.
     *									   <pre>
      pre:
        //
        // The Title field is not empty.
        //
        (event.title != null && event.title.length() >= 1)

            &&

        //
        // The startOrDueDate field is a valid date value.
        //
        (event.startOrDueDate != null) && event.startOrDueDate.isValid()

            &&

        //
        // If non-empty, the EndDate field is a valid date value.
        //
        (event.endDate != null) ==> event.endDate.isValid()

            &&

        //
        // The current workspace is not null.
        //
        (calDB.getCurrentCalendar() != null)

            &&

        //
        // No event of same startDate and title is in the current workspace
        // calendar.
        //
        // No event of same startDate and title is in the current calendar.
        //
        ! exists (ScheduledItem item ; 
            calDB.getCurrentCalendar().items.contains(item) ;
                (item.startOrDueDate.equals(event.startOrDueDate)) &&
                item.duration.equals(event.duration) &&
                (item.title.equals(event.title)));

      post:
        //
        // If preconds met, a scheduled item is in the output calendar if
        // and only if it is the new appt to be added or it is in the 
        // input calendar.
        //
        forall (ScheduledItem item;
            calDB'.getCurrentCalendar().items.contains(item) iff
                (item == event ||
                 calDB.getCurrentCalendar().items.contains(item)))

            &&

        //
        // Also, requiresSaving is true in the output calendar.
        //
        calDB.getCurrentCalendar().requiresSaving;
     */
    abstract void scheduleEvent(Event event);

    CalendarDB calDB;
    boolean requiresSaving;
}