(*
 * Module Question defines the objects and operations related to maintaining
 * a Question Bank, the questions that make up a Question Bank, and Question
 * Bank management.
 *)
module QuestionModule;

from QuestionBankModule import QuestionBank;
export	Question, ID, Type, QuestionText, Difficulty, Time, CorrectAnswer, ClassName, Topic, Author, AverageScore, Created, Modified, LastUsed, Notes,
		Answer, ShortAnswer, Essay, Date, Year;

object Question is
	components: id:ID, type:Type, text:QuestionText*, difficulty:Difficulty, time:Time, ca:CorrectAnswer, cn:ClassName, topics:Topic*, author:Author, as:AverageScore, created:Created, modified:Modified, lu:LastUsed, notes:Notes;
	operations: CreateQuestion;
	description: (*
	*);
end Question;

object Answer is tf:TrueFalse or mc:MultipleChoice or m:Matching or fi:FillIn or sa:ShortAnswer or e:Essay or p:Programming
	description: (* different answer types *);
end;

object TrueFalse is boolean
	description: (* answer for True/False question *);
end;

object MultipleChoice is string*, boolean*
	description: (* answer for Multiple Choice question *);
end;

object Matching is string*, Character*
	description: (* answer for Matching question *);
end;

object Character is string
	description: (* substitute for a char primative *);
end;

object FillIn is string
	description: (* answer Fill In question type *);
end;

object ShortAnswer is string*
	description: (* answer for Short Answer question *);
end;

object Essay is string*
	description: (* answer for Essay question *);
end;

object Programming is string
	description: (* answer for Programming question *);
end;

object ID is integer
	description: (* the unique identifier for a question *);
end;

object Type is string
	description: (* the type of question *);
end;

object QuestionText is string
	description: (* the question to be asked *);
end;

object Difficulty is integer
	description: (* the difficulty of the question, a range from 1 to 10 *);
end;

object Time is integer
	description: (* the expected time to answer the question in minutes *);
end;

object CorrectAnswer inherits from Answer
	description: (* the correct answer to the question *);
end;

object ClassName is string
	description: (* the name of the class that the question is intended for *);
end;

object Topic is string
	description: (* the related topic *);
end;

object Author is string
	description: (* the author of the question *);
end;

object AverageScore is number
	description: (* the average score of the students who have taken the test *);
end;

object Created is Date
	description: (* the creation date of the question *);
end;

object Modified is Date
	description: (* the last modified date of the question *);
end;

object LastUsed is Date
	description: (* the date the question was last added to a test *);
end;

object Notes is string
	description: (* notes about the question *);
end;

object Date is Day, Month, Year
	description: (* the format for date *);
end;

object Day is integer
	description: (* the day component of Date *);
end;

object Month is integer
	description: (* the month component of Date *);
end;

object Year is integer
	description: (* the year component of Date *);
end;

(* begin Joe's section *)

operation CreateQuestion
	inputs:  id:ID, tp:Type, text:QuestionText*, df:Difficulty, tm:Time, ca:CorrectAnswer, cls:ClassName, top:Topic*, auth:Author, crtd:Created, mdf:Modified, nts:Notes;
	outputs:  q:Question;
	description: (*	combines all the fields that make up a Question into a Question object *);
	precondition:
		(* Type must be valid *)
		(tp = "TrueFalse" or tp = "MultipleChoice" or tp = "Matching" or tp = "FillIn" or tp = "ShortAnswer" or tp = "Essay" or tp = "Programming") and
		(* QuestionText cannot be blank *)
		#text >= 1 and
		(* Difficulty must be chosen and must be an integer between 1 and 10, inclusive *)
		df >= 1 and df <= 10 and
		(* Time cannot be blank and must be positive *)
		tm >= 1 and
		(* CorrectAnswer must be chosen/inputted *)
		ca != nil and
		(* ClassName cannot be blank *)
		#cls >= 1;
	postcondition:
		(* A question object is properly created *)
		q.id = id and q.type = tp and q.text = text and q.difficulty = df and q.time = tm and q.ca = ca and q.cn = cls and q.topics = top and q.author = auth and q.created = crtd and q.modified = mdf and q.notes = nts;
end CreateQuestion;


operation AddQuestion
	inputs: q:Question, qb:QuestionBank; 
	outputs: qb':QuestionBank;
	description: (* returns the QuestionBank (qb') with the new Question added *);
    precondition:
		(* The QuestionBank exists *)
		qb != nil and
		(* The Question exists (it was created properly) *)
		q != nil and
		(* The ID of the new Question does not refer to a question already in QuestionBank. *)
		contains(qb, q.id) = false;
	postcondition:
		(* The QuestionBank is returned with the new Question added *)
		(forall (q':Question) 
			(q' in qb') iff ((q' in qb) or q' = q));
end AddQuestion;


operation EditQuestion
	inputs: qb:QuestionBank, q:Question, id:ID;
	outputs: qb':QuestionBank;
	description:  (* changes the Question in the QuestionBank with the matching ID to the new inputted Question, and returns the updated QuestionBank *);
	precondition:
		(* The QuestionBank exists *)
		qb != nil and
		(* The Question exists (it was created properly) *)
		q != nil and
		(* The ID of the new Question refers to a question already in QuestionBank, and the Type has not changed. *)
		(exists (q' in qb) ((q'.id = id) and (q'.type = q.type)));	
	postcondition:
		(* The QuestionBank (qb') now contains the edited Question *)
		q in qb'
		and
		(* The edited Question is in the QuestionBank if and only if it was previously in the QuestionBank and is not the old question,or if it is the edited Question.	*)
		(forall (q':Question) ((q' in qb') iff ((q' in qb and q'.id != id) or q' = q)));
end EditQuestion;

(*
 * Auxiliary Functions.
 *)
function contains(qb:QuestionBank, id:ID)->(boolean) = exists (q in qb.questions) (q.id = id);

(* end Joe's section *)

end QuestionModule;