MODULE Models;
(**

   project   = "BlackBox"
   organization   = "www.oberon.ch"
   contributors   = "Oberon microsystems"
   version   = "System/Rsrc/About"
   copyright   = "System/Rsrc/About"
   license   = "Docu/BB-License"
   changes   = ""
   issues   = ""

**)

   
   IMPORT Kernel, Stores, Sequencers;
   CONST

      minVersion = 0; maxVersion = 0;
      clean* = Sequencers.clean;

      notUndoable* = Sequencers.notUndoable;
      invisible* = Sequencers.invisible;
   TYPE

      Model* = POINTER TO ABSTRACT RECORD (Stores.Store)
         era: INTEGER;   (* stable era >= x *)
         guard: INTEGER   (* = TrapCount()+1 if model is addressee of ongoing broadcast *)
      END;
      Context* = POINTER TO ABSTRACT RECORD END;

      Proposal* = ABSTRACT RECORD END;

      Message* = ABSTRACT RECORD


         model-: Model;
         era-: INTEGER
      END;
      NeutralizeMsg* = RECORD (Message) END;

      UpdateMsg* = EXTENSIBLE RECORD (Message) END;

      

   VAR domainGuard: INTEGER;   (* = TrapCount()+1 if domain is addressee of ongoing domaincast *)
   (** Model **)


   PROCEDURE (m: Model) Internalize- (VAR rd: Stores.Reader), EXTENSIBLE;

      VAR thisVersion: INTEGER;
   BEGIN
      m.Internalize^(rd);
      IF rd.cancelled THEN RETURN END;
      rd.ReadVersion(minVersion, maxVersion, thisVersion)
   END Internalize;
   PROCEDURE (m: Model) Externalize- (VAR wr: Stores.Writer), EXTENSIBLE;

   BEGIN
      m.Externalize^(wr);
      wr.WriteVersion(maxVersion)
   END Externalize;
   
   (** Context **)

   PROCEDURE (c: Context) ThisModel* (): Model, NEW, ABSTRACT;

   PROCEDURE (c: Context) Normalize* (): BOOLEAN, NEW, ABSTRACT;
   PROCEDURE (c: Context) GetSize* (OUT w, h: INTEGER), NEW, ABSTRACT;
   PROCEDURE (c: Context) SetSize* (w, h: INTEGER), NEW, EMPTY;
   PROCEDURE (c: Context) MakeVisible* (l, t, r, b: INTEGER), NEW, EMPTY;
   PROCEDURE (c: Context) Consider* (VAR p: Proposal), NEW, EMPTY;
   (** miscellaneous **)


   PROCEDURE Era* (m: Model): INTEGER;

   BEGIN
      ASSERT(m # NIL, 20);
      RETURN m.era
   END Era;
   PROCEDURE CopyOf* (m: Model): Model;


   BEGIN
      ASSERT(m # NIL, 20);
      RETURN Stores.CopyOf(m)(Model)
   END CopyOf;
   PROCEDURE BeginScript* (m: Model; name: Stores.OpName; OUT script: Stores.Operation);

   (** post: (script # NIL) iff (m.domain # NIL) **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20);
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            seq.BeginScript(name, script)
         ELSE
         END
      ELSE script := NIL
      END
   END BeginScript;
   PROCEDURE Do* (m: Model; name: Stores.OpName; op: Stores.Operation);

   (** pre: m # NIL, op # NIL, ~op.inUse **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20); ASSERT(op # NIL, 21); (* ASSERT(~op.inUse, 22); *)
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            seq.Do(m, name, op)
         ELSE
            op.Do
         END
      ELSE
         op.Do
      END
   END Do;
   PROCEDURE LastOp* (m: Model): Stores.Operation;

   (** pre: m # NIL **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20);
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            RETURN seq.LastOp(m)
         ELSE
            RETURN NIL
         END
      ELSE
         RETURN NIL
      END
   END LastOp;
   PROCEDURE Bunch* (m: Model);

   (** pre: m # NIL, m.Domain() # NIL **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20); ASSERT(m.Domain() # NIL, 21);
      seq := m.Domain().GetSequencer();
      ASSERT(seq # NIL, 22);
      WITH seq: Sequencers.Sequencer DO
         seq.Bunch(m)
      ELSE
      END
   END Bunch;
   PROCEDURE StopBunching* (m: Model);

   (** pre: m # NIL **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20);
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            seq.StopBunching
         ELSE
         END
      END
   END StopBunching;
   PROCEDURE EndScript* (m: Model; script: Stores.Operation);

   (** pre: (script # NIL) iff (m.seq # NIL) **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20);
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         ASSERT(script # NIL, 21);
         WITH seq: Sequencers.Sequencer DO
            seq.EndScript(script)
         ELSE
            ASSERT(script = NIL, 21)
         END
      ELSE
         ASSERT(script = NIL, 21)
      END
   END EndScript;
   PROCEDURE BeginModification* (type: INTEGER; m: Model);


   (** pre: m # NIL **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20);
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            seq.BeginModification(type, m)
         ELSE
         END
      END
   END BeginModification;
   PROCEDURE EndModification* (type: INTEGER; m: Model);

   (** pre: m # NIL **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20);
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            seq.EndModification(type, m)
         ELSE
         END
      END
   END EndModification;
   PROCEDURE SetDirty* (m: Model);

   (** pre: m # NIL **)
      VAR seq: ANYPTR;
   BEGIN
      ASSERT(m # NIL, 20);
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            seq.SetDirty(TRUE)
         ELSE
         END
      END
   END SetDirty;
   PROCEDURE Domaincast* (d: Stores.Domain; VAR msg: Message);

      VAR g: INTEGER; seq: ANYPTR;
   BEGIN
      IF d # NIL THEN
         seq := d.GetSequencer();
         IF (seq # NIL) & (seq IS Sequencers.Sequencer) THEN
            msg.model := NIL; msg.era := -1;
            g := Kernel.trapCount + 1;
            IF domainGuard > 0 THEN ASSERT(domainGuard # g, 20) END;
            domainGuard := g;
            seq(Sequencers.Sequencer).Handle(msg);
            domainGuard := 0
         END
      END
   END Domaincast;
   PROCEDURE Broadcast* (m: Model; VAR msg: Message);

   (** pre: model # NIL **)
   (** post: model.era > model.era', msg.model = model, msg.era = model.era' + 1,
      model.seq # NIL => msg sent to seq **)
      VAR seq: ANYPTR; g: INTEGER;
   BEGIN
      ASSERT(m # NIL, 20);
      msg.model := m;
      IF m.Domain() # NIL THEN seq := m.Domain().GetSequencer() ELSE seq := NIL END;
      IF seq # NIL THEN
         WITH seq: Sequencers.Sequencer DO
            INC(m.era); msg.era := m.era;
            g := Kernel.trapCount + 1;
            IF m.guard > 0 THEN ASSERT(m.guard # g, 21) END;
            m.guard := g;
            seq.Handle(msg);
            m.guard := 0
         ELSE
         END
      END
   END Broadcast;
BEGIN

   domainGuard := 0
END Models.