(**
 * This file defines objects and operations pertinent to the EClass editor.
 * See section 2.2.
 *)

module Editor;

export Editor;

obj Editor is html:HTMLContent and num:Numbering and f:Fold*;

op addFold is
    inputs: e:Editor, f:Fold;
    outputs: e':Editor;
    precondition: (* f.startLine is between 0 and f.endLine, f.endLine
      is less than or equal to the number of lines in the lecture, f may not
      overlap with other folds in e 
      *)
        0 < f.startLine and f.startLine < f.endLine
            and
        f.endLine <= e.num.numLine
            and
        forall (otherF:Fold | otherF in e.f)
            if (otherF.startLine < f.startLine and f.startLine <
              otherF.endLine)
            then (f.endLine < otherF.endLine)
            else if (f.startLine < otherF.startLine)
            then (f.endLine < otherF.startLine);
    postcondition: (* f is added to e' with each Fold arranged so that the next
      Fold in the list has a larger startLine value*)
        (f in e'.f)
            and
        forall (i:integer | i > 0 and i < (#e'.f)-1)
            e'.f[i].startLine < e'.f[i+1].startLine;
    description: (* Adds the given Fold object f to the Editor object *);
end addFold;

op getFold is
    inputs: e:Editor, sl:integer;
    outputs: f':Fold;
    precondition: (* sl is between 1 and the number of lines in the lecture
      inclusive *)
        0 < sl and sl <= e.num.numLine;
    postcondition: (* the Fold object f' that has a startLine matching sl is
      returned or null if no such Fold object exists *)
        (exists (f in e.f) (f.startLine = sl) and (f' = f))
            or
        (not (exists (f in e.f) (f.startLine = sl)) and (f' = nil));
    description: (* Retrieves the Fold object with the same startLine as the
      given integer *);
end getFold;

op deleteFold is
    inputs: e:Editor, f:Fold;
    outputs: e':Editor;
    precondition: (* The given Fold object is in e *)
        f in e.f;
    postcondition: (* A Fold object is in the output Editor if and only if it is
      not the existing Fold to be deleted and it is in the input Editor *)
        (forall (f':Fold)
            (f' in e'.f) iff ((f' != f) and (f' in e.f)));
    description: (* Removes the given fold from the Editor object *);
end deleteFold;

(* --- begin HTMLContent defintion --- *)

obj HTMLContent is HTMLItem*;
obj HTMLItem is Tag or string;
obj Tag is TagName:string and Attribute*;
obj Attribute is AttrName:string and Value;
obj Value is string or real or integer;

op addHTMLItem is
    inputs: e:Editor, h:HTMLItem;
    outputs: e':Editor;
    precondition: ;
    postcondition: (* Given HTMLItem will be appended to the end of the
      HTMLContent in Editor *)
        (h in e'.html)
            and
        e'.html[#e'.html] = h;
    description: (* Adds the given HTMLItem to the Editor object *); 
end addHTMLItem;

op getHTMLItem is
    inputs: e:Editor, ndx:integer;
    outputs: h:HTMLItem;
    precondition: (* ndx is between 1 and the number of HTMLItems in the Editor
      inclusive *)
        0 < ndx and ndx <= (#e.html);
    postcondition: (* The HTMLItem at the given index location is returned *)
        h = e.html[ndx];
    description: (* Retrieve the HTMLItem from HTMLContent in Editor given the
      index location. *);
end getHTMLItem;

op changeHTMLItem is
    inputs: e:Editor, ndx: integer, h:HTMLItem;
    outputs: e':Editor;
    precondition: (* ndx is between 1 and the number of HTMLItems in the Editor
      inclusive. *)
        0 < ndx and ndx <= (#e.html);
    postcondition: (* HTMLItem at ndx in the HTMLContent in the Editor has been
      modified to the given HTMLItem. *)
        e'.html[ndx] = h;
    description: (* Changes the HTMLItem at the given location to the given
      HTMLItem. *);
end changeHTMLItem;

op deleteHTMLItem is
    inputs: e:Editor, ndx:integer;
    outputs: e':Editor;
    precondition: (* ndx is between 1 and the number of HTMLItems in the
      Editor inclusive. *)
        0 < ndx and ndx <= (#e.html);
    postcondition: (* The HTMLItem at ndx is removed from the HTMLContent in
      Editor.  All HTMLItems with a higher index are moved down to fill in the
      gap. *)
        not (e.html[ndx] in e'.html)
            and
        forall (i:integer | i > ndx and i < (#e'.html))
            e'.html[i-1] = e'.html[i];
    description: (* Removes the HTMLItem in the HTMLContent in Editor at index
      ndx. *);
end deleteHTMLItem;

(* --- End HTMLContent definition --- *)

(* --- Start Numbering definition --- *)

obj Numbering is numLine:integer and curLine:integer;

op getNumLine is
    inputs: e:Editor;
    outputs: i:integer;
    precondition: ;
    postcondition: (* Returns numLine from Editor. *)
        i = e.num.numLine;
    description: (* Retrieves the value of component numLine from Numbering
      instance in Editor *);
end getNumLine;

op getCurLine is
    inputs: e:Editor;
    outputs: i:integer;
    precondition: ;
    postcondition: (* Returns curLine from Editor. *)
        i = e.num.curLine;
    description: (* Retrieves the value of component curLine from Numbering
      instance in Editor *);
end getCurLine;

op setNumLine is
    inputs: e:Editor, i:integer;
    outputs: e':Editor;
    precondition: (* Given integer is greater than 0 *)
        i > 0;
    postcondition: (* Editor object e' is returned with its Numbering's numLine
      property set to the given integer.  All other components of e' are
      identical to e. *)
      e'.num.numLine = i
        and
      e.html = e'.html and e.f = e'.f and e.num.curLine = e'.num.curLine;
    description: (* Sets component numLine of Numbering in Editor to given
      integer *);
end setNumLine;

op setCurLine is 
    inputs: e:Editor, i:integer;
    outputs: e':Editor;
    precondition: (* Given integer is between 1 and e.num.numLine inclusive *)
        0 < i and i <= e.num.numLine;
    postcondition: (* An Editor object e' is returned with e'.Numbering.curLine
      set to the given integer.  All other components of e' are identical to
      that of e. *)
      e'.num.curLine = i
        and
      e.html = e'.html and e.f = e'.f and e.num.numLine = e'.num.numLine;
    description: (* Sets component curLine of e'.Numbering to given integer *);
end setCurLine;

(* --- End Numbering definition --- *)

(* --- Start Fold definition --- *)

obj Fold is startLine:integer and exp:boolean and endLine:integer;

op getStartLine is
    inputs: f:Fold;
    outputs: i:integer;
    precondition: ;
    postcondition: (* Returns value of startLine from given Fold object *)
        i = f.startLine;
    description: (* Retrieves component startLine from Fold *);
end getStartLine;

op getEndLine is
    inputs: f:Fold;
    outputs: i:integer;
    precondition: ;
    postcondition: (* Returns the value of endLine from given Fold object *)
        i = f.endLine;
    description: (* Retrieves component endLine from Fold *);
end getEndLine;

op getExp is 
    inputs: f:Fold;
    outputs: b:boolean;
    precondition: ;
    postcondition: (* Returns the value of exp from given Fold object *)
        b = f.exp;
    description: (* Retrieves component exp from Fold *);
end getExp;

op setExp is
    inputs: f:Fold, b:boolean;
    outputs: f':Fold;
    precondition: ;
    postcondition: (* Returns Fold object f' with exp set to given boolean b.
      All other components of f' are identical to the given Fold f. *)
        f'.exp = b
            and
        f.startLine = f'.startLine and f.endLine = f'.endLine;
    description: (* Sets the exp component of Fold to the given boolean *);
end setExp;

(* --- End Fold definition --- *)

end Editor;