module Tests;

  from Questions import Question, PointValue;
  from StudentFunctionality import Student;
  from MainUIFunctionality import all;
  export all;

  object Test is 
    components: questions:QuestionGroup*, time:timeLimit;
    description: (*
      A Test represents a collection of questions that have been compiled
      into a test that can be taken, and the time limit for the test.
    *);
  end Test;
  
  object timeLimit is integer;

  object QuestionGroup is 
    components: h:Header and qs:Question*;
    description: (*
      A question group is a group of questions with a header to 
      give special instructions for the questions.
    *);
  end QuestionGroup;

  object Header is string;

  object PracticeTest inherits from Test
    description: (*
      A PracticeTest is a test that has been made for the purpose of
      helping a student. It does not require a proctor.
    *);
  end PracticeTest;

  object ProctorTest inherits from Test
    description: (*
      A ProctorTest represents a test that has been made, and is ready
      to be proctored to students.
    *);
  end ProctorTest;

  object TakenTest inherits from Test
    components: answers:GivenAnswer*, flag:InProgress, name:Student;
    description: (*
      A TakenTest object is a test that has been taken, but not yet 
      graded. It contains all the questions from the test, as well 
      as the student's answers.
    *);
  end TakenTest;

  object GivenAnswer is string;
  object FormattedAnswer inherits from GivenAnswer
    components: fa:FormattedAnswerText;
  end;
  object FormattedAnswerText is s:string or fs:FormattedString;
  object InProgress is boolean;
  object SubmittedTests is TakenTest*;

  object GradedTest inherits from TakenTest
    components: s:Score*, g:Grade, c:Comment*;
    description: (*
      A GradedTest is a test that has been taken and graded. It 
      contains the score recieved on each question and the total grade, 
      along with the components of a TakenTest.
    *);
  end GradedTest;

  object Score is real;
  object Grade is real;
  object Comment is string;

  operation CreateBlankTest is
    outputs: t:Test;
    description: (*
      Creates a new, empty test.
    *);
  end;

  operation InsertQuestionGroup is
    inputs: t:Test, qg:QuestionGroup;
    outputs: t':Test;
    description: (*
      Adds the given question group to the test.
    *);
    postcondition:
      (*
       * The question group is now part of the test, and the test is 
       * otherwise unchanged.
       *)
      forall (qg':QuestionGroup)
        (qg' in t'.questions) iff ((qg' = qg) or (qg' in t.questions));
  end;

  operation DeleteQuestionGroup is
    inputs: t:Test, qg:QuestionGroup;
    outputs: t':Test;
    description: (*
      Removes the given question group from the test.
    *);
    postcondition:
      (*
       * The question group no longer part of the test, and the test is 
       * otherwise unchanged.
       *)
      forall (qg':QuestionGroup)
        (qg' in t'.questions) iff ((qg' != qg) or (qg' in t.questions));
  end;

  operation SetPointValue is
    inputs: q:Question, pv:PointValue;
    outputs: q':Question;
    description: (*
      Sets the point value of the given question.
    *);
    postcondition:
      (*
       * The output Question has the point value, pv.
       *)
      q'.pv = pv;
  end;

end Tests;