(****
 *
 * This file defines objects and operation related to adding, editing, and deleting items.
 *
 * See Sections 2.5 of the Milestone 4 requirements.
 *
 *)
module item;
from class import Class;
export all;

object Item is
	components: children:Item* and parentitem:Item and name:Name and weightandrawscore:WeightAndRawScore and ParentWeightAndRawScore and
			LatePolicy and DueDate and LatePolicyOptions and collapsed:Collapsed;
	description: (*
		An Item is used by an instructor to determine grades for students.
		An Item is basically a graded item which can contain sub-items which are also Items.
	*);
end Item;

object Name is string;
object Collapsed is boolean;

object WeightAndRawScore is
	components: weight:Weight and rawscore:RawScore;
	description: (*
		This displays information about the current weight and raw score of the item.
	*);
end WeightAndRawScore;

object Weight is integer;
object RawScore is integer;

object ParentWeightAndRawScore is
	components: ParentWeight and ParentRawScore;
	description: (*
		This displays information about the parent weight and raw score of the item.
	*);
end ParentWeightAndRawScore;

object ParentWeight is integer;
object ParentRawScore is integer;

object LatePolicy is
	components: AllowLatePolicy and OverrideParent;
	description: (*
		This displays whether there is a late policy in effect or not.
	*);
end LatePolicy;

AllowLatePolicy is boolean;
OverrideParent is boolean;

object DueDate is
	components: Month and Day and Year;
	description: (*
		This displays the due date of the item.
	*);
end DueDate;

object Month is integer;
object Day is integer;
object Year is integer;

object LatePolicyOptions is
	components: AllowItemLate and AllowGraceDay and DailyDecay and MaxNumberOfUnit and
			DeductionEachUnitLate;
	description: (*
		This displays additional options for the late policy.
	*);
end LatePolicyOptions;

object AllowGraceDay is boolean;
object AllowItemLate is boolean;
object DailyDecay is boolean;
object MaxNumberOfUnit is integer;
object DeductionEachUnitLate is integer;

function SumWeight(l:Item*) =
	if (#l = 0) then 0
	else l[1].weightandrawscore.weight + SumWeight(l[2:#1]);

operation AddItem is
	inputs: class:Class, item:Item;
	outputs: class':Class;

	description: (*
		This operation adds the created item into the class.
	*);

	precondition:
		(*
		 * There are no items in the input class with the same item name as the
		 * item to be added.
		 *)
		(not (exists (item' in class.it) item'.name = item.name));

	postcondition:
		(*
		 * The given item is in the output class.
		 *)
		(item in class'.it)

			and
		(*
		 * If parentitem is not nil, then the total sum of the weights of items under parentitem must equal 100.
		 *)
		if (item.parentitem != nil) then
			(SumWeight(item.parentitem.children) = 100)

			and

		(*
		 * An item is in the output class if and only if it is the 
		 * new item to be added or it is in the input class.
		 *)
		forall (item':Item)
			(item' in class'.it) iff ((item' = item) or (item' in class.it));

end AddItem;

operation EditItem is
	inputs: class:Class, item:Item;
	outputs: class':Class;

	description: (*
		This operation Edits an existing item that is in the class.
	*);

	precondition:
		(*
		 * The given item is in the class
		 *)
		(item in class.it);

	postcondition:
		(*
		 * The given item is in the output class.
		 *)
		(item in class'.it)

			and

		(*
		 * If parentitem is not nil, then the total sum of the weights of items under parentitem must equal 100.
		 *)
		if (item.parentitem != nil) then
			(SumWeight(item.parentitem.children) = 100)

			and

		(*
		 * An item is in the output class if and only if it is the 
		 * item that was edited or it is in the input class.
		 *)
		forall (item':Item)
			(item' in class'.it) iff ((item' = item) or (item' in class.it))
	
			and

		(*
		 * An item in the output class cannot have the same name as another item
		 * in the output class.
		 *)
		(not (exists (item' in class'.it) item'.name = item.name));

end EditItem;

		
operation DeleteItem is
	inputs: class:Class, item:Item;
	outputs: class':Class;

	description: (*
		This operation Deletes an existing item that is in the class.
	*);

	precondition:
		(*
		 * The given item is in the class
		 *)
		(item in class.it);

	postcondition:
		(*
		 * An item is in the output class if and only if it is not the
                 * existing item to be deleted and it is in the input class.
		 *)
		forall (item':Item)
			(item' in class'.it) iff ((item' != item) and (item' in class.it));

end DeleteItem;

end item;