(*
 * Module QuestionBank defines the objects and operations related to maintaining
 * a Question Bank and Question Bank management.
 *)
module QuestionBankModule;

from QuestionModule import Question, ID, Type, QuestionText, Difficulty, Time, CorrectAnswer, ClassName, Topic, Author, AverageScore, Created, Modified, LastUsed, Notes, Answer;
export	QuestionBank, LocalQuestionBank, SharedQuestionBank,
		QuestionBankSettings, LocalQuestionBankSettings, SharedQuestionBankSettings,
		LocalBankProfile, BankName, BankLocation,
		SharedBankProfile, BankUserList, BankAdministrator, BankUR, Username, Password;

object QuestionBank is
	components: questions:Question*;
	operations: AddQuestion, EditQuestion, DeleteQuestions, FilterQuestions;
	description: (*
	*);
end QuestionBank;

object LocalQuestionBank inherits from QuestionBank
	operations: UploadQuestion;
	description: (*
	*);
end LocalQuestionBank;

object SharedQuestionBank inherits from QuestionBank
	components: BankUserList;
	operations: DownloadQuestion;
	description: (*
	*);
end SharedQuestionBank;

(*---------------------- begin Kate's section -----------------------*)

operation DeleteQuestions is 
	inputs: id: ID*, a: Author*, qb: QuestionBank;
	outputs: qb' : QuestionBank;
	description: (* This operation delets questions from the QuestionBank.  It return a QuestionBank without the deleted questions in it *);
	precondition: 
		(forall (idx in id)
			(forall (authx in a)
			(forall (q: Question)
			exists (q in qb )((q.id = idx) and (q.author = authx) ) )));
	postcondition: 
		(*The unwanted quesitons are deleted from the QuestionBank*)
		(forall (q': Question)
		(forall (authx in a)
		(forall (idx in id)
			(q' in qb') iff ((idx != q'.id) and (authx != q'.author) and (q' in qb)))));
end DeleteQuestions;

operation UploadQuestions is
	inputs: id: ID*, a: Author*, lqb: LocalQuestionBank, sqb: SharedQuestionBank;
	outputs: sqb' : SharedQuestionBank;		
	description: (*This operation uploads questions from the local QuestionBank to the shared QuestionBank; after 
			the operation, the shared question bank will now contain the new questions*);
	precondition: 
		(forall (q: Question)
		(forall (authx in a)
		(forall (idx in id)
		exists (q in lqb) ((q.id = idx) and (q.author = authx)))));	
	postcondition:
		(*The desired questions are uploaded from the shared QuestionBank to the local QuestionBank*)
		(forall (q' : Question)
		(forall (authx in a)
		(forall (idx in id)
			(q' in sqb'.questions) iff ((q' in sqb.questions) or ((idx =q'.id) and (authx = q'.author) and (q' in lqb.questions))))));
end UploadQuestions;

operation DownloadQuestions is
	inputs: id: ID*, a: Author*, lqb: LocalQuestionBank, sqb: SharedQuestionBank;
	outputs: lqb': LocalQuestionBank;
	description: (*This operation downloads questions from the shared QuestionBank to the local QuestionBank, after
			this operation is performed the local question bank will contain the new questions*);
	precondition:
		(forall (q: Question)
		(forall (authx in a)
		(forall (idx in id)
			exists (q in sqb.questions) ((q.id = idx) and (q.author = authx)))));
	postcondition:
		(*The desired questions are downloaded from the local QuestionBank to the shared QuestionBank*)
		(forall (q' : Question)
		(forall (authx in a)
		(forall (idx in id)
			(q' in lqb'.questions) iff ((q' in lqb.questions) or ((idx =q'.id) and (authx = q'.author) and (q' in sqb.questions))))));
end DownloadQuestions;

operation FilterQuestions is 
	inputs: qb: QuestionBank, f: Filter;
	outputs: ql : QuestionList;
	description: (* Displays questions with certain selected attributes, while hiding others*);
	precondition:
		qb != nil;
	postcondition:
		(*Undesired questions are hidden, while the desired questions are displayed*)
		(forall(q: Question)
		exists (q in qb.questions)
			(q.cn in f.class) or
			(q.author in f.author) or
			(q.type in f.type) or
			((*f.keyword in q.notes*) (f.keyword in q.topics)));
end FilterQuestions;

operation ShowDetails is
	inputs: q: Question;
	outputs: dp: DetailPanel;
	description: (*Clicking on the question row fills in the ShowDetails *);
	precondition:
		(forall(qb: QuestionBank) 
			(q in qb));
	postcondition: 
		(*The DetailPanel is filled in with the attributes of the selected question*)
		(dp.answer = q.ca) and
		(dp.class = q.cn) and
		(dp.topics = q.topics) and
		(dp.author = q.author) and
		(dp.as = q.as) and
		(dp.created = q.created) and
		(dp.modified = q.modified) and
		(dp.lu = q.lu) and
		(dp.notes = q.notes);
end ShowDetails;

operation SortedByTypeAscending is
	inputs: ql:QuestionList;
	outputs: ql':QuestionList;
	description: (* Sorts the list of questions by their type field *);
	precondition:
		#ql > 0;
	postcondition:
		(forall (q:Question) ((q in ql) iff (q in ql'))) and
		(forall (i:integer | i >= 1 and i < #ql)
			(ql'[i].type < ql'[i + 1].type));
end SortedByTypeAscending;

operation SortedByDifficultyAscending is
	inputs: ql:QuestionList;
	outputs: ql':QuestionList;
	description: (* Sorts the list of questions by their difficulty field *);
	precondition:
		#ql > 0;
	postcondition:
		(forall (q:Question) ((q in ql) iff (q in ql'))) and
		(forall (i:integer | i >= 1 and i < #ql)
			(ql'[i].difficulty < ql'[i + 1].difficulty));
end SortedByDifficultyAscending;

operation SortedByTimeAscending is
	inputs: ql:QuestionList;
	outputs: ql':QuestionList;
	description: (* Sorts the list of questions by their time field *);
	precondition:
		#ql > 0;
	postcondition:
		(forall (q:Question) ((q in ql) iff (q in ql'))) and
		(forall (i:integer | i >= 1 and i < #ql)
			(ql'[i].time < ql'[i + 1].time));
end SortedByTimeAscending;

operation SortedByQuestionAscending is
	inputs: ql:QuestionList;
	outputs: ql':QuestionList;
	description: (* Sorts the list of questions by their question text field *);
	precondition:
		#ql > 0;
	postcondition:
		(forall (q:Question) ((q in ql) iff (q in ql'))) and
		(forall (i:integer | i >= 1 and i < #ql)
			(ql'[i].text < ql'[i + 1].text));
end SortedByQuestionAscending;

object DetailPanel is answer:Answer, class:ClassName, topics:Topic*, author:Author, as:AverageScore, created:Created, modified:Modified, lu:LastUsed, notes:Notes
	description: (*The Detail Panel lists more detailed question attributes*);
end;

object Filter is type:TypeFilter, class:ClassFilter, author:AuthorFilter, keyword:KeywordFilter
	description: (* Each filter type allows the user to display or hide questions 
			posessing that property*);
end;

object TypeFilter is Type*
	description: (* TypeFilter allows the user to hide or display questions based on 
			what answer type is assosiated with the question*);
end;

object ClassFilter is ClassName*
	description: (* ClassFilter allows the user to hide or display questions based on 
			what class name is assosiated with the question*);
end;

object AuthorFilter is Author*
	description: (* AuthorFilter allows the user to hide or display questions based on 
			the author of the question*);
end;

object KeywordFilter is string
	description: (* KeywordFilter allows the user to hide or display questions based on 
			what words are contained in the question text filed, the notes field,
			and the answer field*);
end;

object QuestionList is Question*
	description: (* This is the list of questions that is displayed in the table of the question bank view.
					This way, you can filter and sort the questions without affecting the underlying Question Bank,
					since those only affect how they are shown to the user. *);
end;
	
(*--------------------------- end Kate's section -----------------------*)


(*--------------------------- begin Adam's section ---------------------*)

object QuestionBankSettings is Lqbs:LocalQuestionBankSettings, Sqbs:SharedQuestionBankSettings;

object LocalQuestionBankSettings is 
	components:  Lbp:LocalBankProfile*;
	operations:  AddLocalQB, EditLocalQB, DeleteSharedQB, MoveUp, MoveDown;
end LocalQuestionBankSettings;
object LocalBankProfile is bn:BankName, bl:BankLocation, lqb:LocalQuestionBank;

object SharedQuestionBankSettings is 
	components:  sbp:SharedBankProfile*;
	operations:  AddSharedQB, EditSharedQB, DeleteSharedQB, MoveUp, MoveDown;
end SharedQuestionBankSettings;
object SharedBankProfile is bn:BankName, bl:BankLocation,  bul:BankUserList, un:Username, pw:Password, sqb:SharedQuestionBank;
object BankUserList is
	components: bur:BankUR*, ba:BankAdministrator; 
	operations: AddUser, DeleteUser, SwitchAdmin;
end BankUserList;

object BankUR is un:Username, pw:Password;
object Username is string;
object ConfirmPassword is string;
object Password is string;
object BankName is string;
object BankLocation is string;
object BankAdministrator is string;

operation CreateLocal is
	inputs: Lqbs:LocalQuestionBankSettings, BankName, BankLocation;
	outputs: Lqbs':LocalQuestionBankSettings;
end CreateLocal;

operation AddSharedQB is
	inputs: Sqbs: SharedQuestionBankSettings, sbp:SharedBankProfile;
	outputs: Sqbs':SharedQuestionBankSettings;
	precondition:
		(* BankName, BankLocation, Username, and Password are non-empty  *)
		#(sbp.bn) >= 1 and
		#(sbp.bl) >= 1 and
		#(sbp.un) >= 1 and
		#(sbp.pw) >= 1;
	postcondition:
		(* Question Bank is added to the Shared Question Bank list *)
		sbp in Sqbs';
end AddSharedQB;

operation AddLocalQB is
	inputs: Lqbs:LocalQuestionBankSettings, lbp:LocalBankProfile;
	outputs: Lqbs':LocalQuestionBankSettings;
	precondition:
		(* BankName, BankLocation are non-empty *)
		#(lbp.bn) >= 1 and
		#(lbp.bl) >= 1;
	postcondition:
		(* Question Bank is added to the Local Question Bank list *)
		lbp in Lqbs';
end AddLOcalQB;

operation EditSharedQB is
	inputs: Sqbs:SharedQuestionBankSettings, sbp:SharedBankProfile;
	outputs: Sqbs':SharedQuestionBankSettings;
	precondition:
		(* BankName, BankLocation, Username, and Password are non-empty *)
		#(sbp.bn) >= 1 and
		#(sbp.bl) >= 1 and
		#(sbp.un) >= 1 and
		#(sbp.pw) >= 1;
	postcondition:
		(* Question Bank is updated in the Shared Question Bank list *)
		sbp in Sqbs';
end EditSharedQB;

operation EditLocalQB is
	inputs: Lqbs:LocalQuestionBankSettings, lbp:LocalBankProfile;
	outputs: Lqbs':LocalQuestionBankSettings;
	precondition:
		(* BankName, BankLocation are non-empty *)
		#(lbp.bn) >= 1 and
		#(lbp.bl) >= 1;
	postcondition:
		(* Question Bank is updated in the Shared Question Bank list *)
		lbp in Lqbs';
end EditLocalQB;

operation DeleteLocalQB is
	inputs: Lqbs:LocalQuestionBankSettings, Lbp:LocalBankProfile;
	outputs: Lqbs':LocalQuestionBankSettings;
	precondition:
		(* Question Bank must exist in Question Bank Settings *)
		Lbp != nil and
		Lbp in Lqbs;
	postcondition:
		(* Question Bank is deleted from Local Question Bank list *)
		not(Lbp in Lqbs');
end DeleteLocalQB;

operation DeleteSharedQB is
	inputs: Sqbs:SharedQuestionBankSettings, Sbp:SharedBankProfile;
	outputs:  Sqbs':SharedQuestionBankSettings;
	precondition:
		(* Question Bank must exist in Question Bank Settings *)
		Sbp != nil and
		Sbp in Sqbs;
	postcondition:
		(* Question Bank is deleted from Shared Question Bank list *)
		not(Sbp in Sqbs');
end DeleteSharedQB;
 
operation AddUser is
	inputs:  bul:BankUserList, un:Username, pw:Password, cpw:ConfirmPassword;
	outputs: bul':BankUserList;
	precondition:
		(* Username cannot exist in bank users list and password and confirm password must match *)
		(not (exists (bur:BankUR) (bur in bul.bur) and (bur.un = un))) and
		(#pw >= 1 and #cpw >=1 and pw = cpw);
	postcondition:
		(* BankUR has been added to bank user list *)
		forall(bur:BankUR)
		(bur in bul'.bur) iff (bur in bul.bur) or ((bur.un) = un and (bur.pw) = pw);
end AddUser;

operation DeleteUser is
	inputs: bul:BankUserList, un:Username;
	outputs: bul':BankUserList;
	precondition:
		(* User must exist in Bank Users List *)
		(un != nil) and (exists(r in bul.bur) (r.un = un));
	postcondition:
		(* User record with matching user name no longer exists in the bank user list *)
		forall(bur:BankUR)
		(bur in bul'.bur) iff ((bur in bul.bur) and (bur.un != un));
end DeleteUser;

operation SwitchAdmin is
	inputs: Bul:BankUserList, bur:BankUR;
	outputs: Bul':BankUserList;
	precondition:
		(* User record needs to be in user list *)
		(* (exists(r in Bul)) r.un = bur.un *);
	postcondition:
		(* User is now the administrator *)
		forall(ur:UserRecord)
			(ur in Bul'.bur) iff (ur in Bul.bur)
		and 
		(Bul'.ba = bur.un);
end SwitchAdmin;

operation FileDirectory is 
	inputs: qb:QuestionBank, ln:BankLocation;
	outputs: ln':BankLocation;
	precondition:
		(* Question Bank must exist in folder *);
	postcondition:
		(* Location is the path of the Question Bank *);
end FileDirectory;

operation MoveUp is
	inputs: sbp:SharedBankProfile, Sqbs:SharedQuestionBankSettings;
	outputs: Sqbs':SharedQuestionBankSettings;
	precondition:
		(* selected profile exists *)
		(exists (sbp' in Sqbs) (sbp.bn = sbp'.bn)) and
		Sqbs[1] != sbp;
	postcondition:
		(* Question Bank moves up on Question Bank list *)
		forall (profile:SharedBankProfile) ((profile in Sqbs) iff (profile in Sqbs'));
end Moveup;

(*----------------------------- end Adam's section --------------------------*)

end QuestionBankModule;