module Sched;

 	from RoomDB import RoomDB, RoomRecord, RoomType;
	from CourseDB import CourseDB, CourseRecord, Component;
	from StudentApp import Summer, Winter, Spring, Fall;
	from Teacher import TeacherDatabase, TeacherRecord, Monday, Tuesday, Wednesday, Thursday, Friday, Teacher_class_pref, Class, Preference_value;
export Schedule, Time, section, ClassAssignment, daysOfWeek, day;

object Schedule is
   components: ca:ClassAssignment*, cd:CourseDB, td:TeacherDatabase, rd:RoomDB,
		term, year, phase, department, f:fairness;
   description: (*
      This object represents a schedule in the admin portion.  
      Schedule includes the databases used to create it.
   *);
end Schedule;


object ClassAssignment is
   components: tr:TeacherRecord, rr:RoomRecord, cr:CourseRecord, t:Time, s:section, staff:STAFF, tba:TBA;
   description: (*
      This object represents the connection made between a teacher, room, CourseRecord, 
         and Time
      Assumes that TeacherRecord, RoomRecord, and CourseRecord are described in another RSL
   *);
end ClassAssignment;


object Time is
   components:  h:Hour, m:Minute, d:Duration, dow:daysOfWeek;
   description: (*
      Time represents the starting time, and how long a class will go.
	And what days too.
   *);
end Time;

object fairness is integer
	description: (*
		A measure of how fair a schedule is.
	*);
end fairness;
object Hour is integer
	description: (*
		The time of day, to the nearest hour, in which the class starts
	*);
end Hour;
object Minute is integer
	description: (*
		The minute of the hour, in which the class starts
	*);
end Minute;
object Duration is integer
	description: (*
		How long the class will last, in minutes
	*);
end Duration;
object daysOfWeek is day*
	description: (*
		container object to hold the days in which class is in session.
	*);
end daysOfWeek;
object day is Monday or Tuesday or Wednesday or Thursday or Friday
	description: (*
		A day of the week, used to tell which days of the week a class runs for
	*);
end day;


object term is Summer or Spring or Winter or Fall
	description: (*
		container object to tell when the schedule is in effect
	*);
end ;
object year is integer
	description: (*
		We would all like to know what year it was for
	*);
end ;
object phase is integer
	description: (*
		phase 1, 2, or 3...  How far along is it?
	*);
end ;
object department is string
	description: (*
		A short string identifying the department
	*);
end ;
object section is integer
	description: (*
		more than one class of a course? need section numbers to differentiate
	*);
end ;
object STAFF is boolean
	description: (*
		when a teacher can't be found, assign staff
	*);
end ;
object TBA is boolean
	description: (*
		when a room can't be found, assign TBA
	*);
end ;


operation GenerateSchedule
	inputs: cdb:CourseDB, TeacherDatabase, RoomDB, Schedule;
	outputs: sc':Schedule;
	post: SchedIsBest(sc') and isComplete(sc') and isAllAssigned(sc', cdb) and teachNotDB(sc') 
		and roomNotDB(sc') and teacherProficient(sc') and roomRight(sc') and isAllUnique(sc');
	description: (*
		GenerateSchedule is the big operation that makes many schedules 
		and prints out the best one
	*);
end GenerateSchedule;


operation NewSchedule
	inputs: cdb:CourseDB, TeacherDatabase, RoomDB;
	outputs: sc':Schedule;
	post: isComplete(sc') and isAllAssigned(sc', cdb) and teachNotDB(sc')
		and roomNotDB(sc') and teacherProficient(sc') and roomRight(sc') and isAllUnique(sc');
	description: (*
		NewSchedule will be the operation for when the admin user selects new 
		from any of the various start points.
	*);
end NewSchedule;

operation fineTuneSchedule
	inputs: Schedule;
	outputs: sc':Schedule;
	post: isComplete(sc') and teachNotDB(sc')
		and roomNotDB(sc') and teacherProficient(sc') and roomRight(sc') and isAllUnique(sc');
	description: (*
		fineTuneSchedule will be the operation for when the admin user makes edits 
		to any of the various components.
	*);
end NewSchedule;


function isAllAssigned(sch:Schedule, CDB:CourseDB) = 
	(forall (c:CourseRecord)
		if(c in CDB)
		then 
		(exists (i:integer | (i >=1) and (i <= #(sch.ca)))
		((sch.ca[i].cr) = c)
		)
	);
	

function isComplete(sch:Schedule) = 
	(forall (i:integer | (i >=1) and (i <= #(sch.ca)))
		(exists (j:integer | (j >=1) and (j <= #(sch.td.q)))
		(exists (k:integer | (k >=1) and (k <= #(sch.cd.cr)))
		(exists (l:integer | (l >=1) and (l <= #(sch.rd.rr)))

		((sch.ca[i].tr = sch.td.q[j]) or sch.ca[i].staff) and
		sch.ca[i].cr = sch.cd.cr[k] and
		(sch.ca[i].rr = (sch.rd.rr[l]) or sch.ca[i].tba)
	))));

function teachNotDB(sch:Schedule) =
	(forall (ca1:ClassAssignment) 
		(forall (ca2:ClassAssignment)
			ca1.t != ca2.t and
			ca1.tr != ca2.tr
		)
	);

function roomNotDB(sch:Schedule) =
	(forall (ca3:ClassAssignment) 
		(forall (ca4:ClassAssignment)
			ca3.t = ca4.t and
			ca3.rr = ca4.rr
		)
	);

function teacherProficient(sch:Schedule) = 
	(forall (i:integer | (i >=1) and (i <= #(sch.ca)))
		(exists (j:integer | (j >=1) and (j <= #(sch.ca)))
			(sch.ca[i].tr.cp.c[j].p > 0) and
			(sch.ca[i].tr.cp.c[j].crd = sch.ca[i].cr)
	));
		
function SchedIsBest(sch:Schedule) =
	(forall (sched:Schedule)
		sch.f >= sched.f
	);

		
function isAllUnique(sch:Schedule) =
	(forall (ca5:ClassAssignment) 
		forall (ca6:ClassAssignment)
			ca5.cr != ca6.cr and
			ca5.s != ca6.s
	);

function roomRight(sch:Schedule) = 
	(forall (i:integer | (i >=1) and (i <= #(sch.ca)))
		sch.ca[i].cr.cp = sch.ca[i].rr.rt
	);


end Sched;