-- SMCommentTableImpl.mesa
-- last edit by Satterthwaite, April 29, 1983 10:19 am
-- last edit by Schmidt, May 3, 1982 3:35 pm

DIRECTORY
  SMCommentTable: TYPE USING [
    BreakSequence, BreakTab, Index, Node, Ref, Table, TableNode, Text],
  SMCommentTableOps: TYPE USING [];

SMCommentTableImpl: CEDAR PROGRAM
    EXPORTS SMCommentTableOps ~ {
  OPEN SMCommentTable;

  CommentManager: PUBLIC TYPE ~ RECORD[
    z: ZONE←,
    commentTable: SMCommentTable.Table←NIL,
    breakTable: SMCommentTable.BreakTab←NIL,
    ending: INT←0];
    
  CommentM: TYPE ~ REF CommentManager;
  
  TableOverflow: PUBLIC ERROR ~ CODE;
  TableOrder: PUBLIC ERROR ~ CODE;

  Create: PUBLIC PROC[zone: ZONE] RETURNS [CommentM] ~ {
    RETURN [zone.NEW[CommentManager ← [z~zone]]]};
    
  Reset: PUBLIC PROC[cm: CommentM] ~ {
    IF cm.commentTable # NIL THEN {
      cm.commentTable.size ← 0;
      cm.commentTable.last ← NIL;
      cm.commentTable.lastx ← 0};
    IF cm.breakTable # NIL THEN cm.breakTable.size ← 0;
    cm.ending ← 0};


  Add: PUBLIC PROC[cm: CommentM, start: Index, text: Text, lastToken,prefix: Index] ~ {
    ref: Ref ~ (cm.z).NEW[Node ← [start, text, lastToken, prefix]];
    size: NAT ~ (IF cm.commentTable = NIL THEN 0 ELSE cm.commentTable.size);
    max:  NAT ~ (IF cm.commentTable = NIL THEN 0 ELSE cm.commentTable.max);
    last: Ref ~ (IF size > 0 THEN cm.commentTable[size-1] ELSE NIL);
    IF lastToken > start THEN RETURN;
    IF last # NIL THEN {
      IF last.start > start OR lastToken < last.lastToken THEN RETURN;
      IF last.start = start THEN {
	-- merge the blank line comment with the actual comment
	IF last.text # NIL THEN RETURN;
	ref.prefix ← prefix + last.prefix;
	cm.commentTable[size-1] ← ref;
	RETURN}
      };      
    IF size >= max THEN {
      newMax: NAT ~ (IF max > (NAT.LAST/2 - 2) THEN NAT.LAST ELSE max*2 + 4);
      IF newMax <= size THEN ERROR TableOverflow;
       {newTable: Table ~ (cm.z).NEW[TableNode[newMax]];
	newTable.last ← NIL;
	FOR i: NAT IN [0..size) DO
	  newTable[i] ← cm.commentTable[i];
	  cm.commentTable[i] ← NIL;
	  ENDLOOP;
	cm.commentTable ← newTable;
	}};
    cm.commentTable[size] ← ref;
    cm.commentTable.size ← size + 1};

  AddBreakHint: PUBLIC PROC[cm: CommentM, index: Index] ~ {
    size: NAT ~ (IF cm.breakTable = NIL THEN 0 ELSE cm.breakTable.size);
    max:  NAT ~ (IF cm.breakTable = NIL THEN 0 ELSE cm.breakTable.max);
    last: Index ~ (IF size = 0 THEN 0 ELSE cm.breakTable[size-1]);
    IF index <= last THEN RETURN;
    IF size >= max THEN {
      -- must extend the table
      new: BreakTab ← NIL;
      nmax: NAT ← MAX[200, IF max < NAT.LAST/2 THEN max+max ELSE NAT.LAST];
      IF nmax <= max THEN ERROR TableOverflow;
      new ← (cm.z).NEW[BreakSequence[nmax]];
      FOR i: NAT IN [0..size) DO new[i] ← cm.breakTable[i]; ENDLOOP;
      cm.breakTable ← new};
    cm.breakTable[size] ← index;
    cm.breakTable.size ← size + 1};   

  FindNext: PUBLIC PROC[cm: CommentM, notBefore: Index] RETURNS[Ref] ~ {
    IF cm.commentTable # NIL AND cm.commentTable.size > 0 THEN {
      low: NAT ← 0;
      high: NAT ← cm.commentTable.size-1;
      last: Ref ← cm.commentTable.last;
      lastx: NAT ← cm.commentTable.lastx;
      lval: Index ← 0;
      IF last # NIL THEN {
	lval ← last.start;
	IF notBefore = lval THEN RETURN [last];
	IF notBefore < lval THEN high ← lastx ELSE low ← lastx+1};
      WHILE low < high DO
	lval ← (last ← cm.commentTable[lastx ← (low+high)/2]).start;
	IF notBefore < lval THEN {high ← lastx; LOOP};
	IF notBefore > lval THEN {low ← lastx+1; LOOP};
	cm.commentTable.lastx ← lastx;
	RETURN [cm.commentTable.last ← last];
	ENDLOOP;
      IF high # lastx OR last = NIL THEN
        lval ← (last ← cm.commentTable[lastx ← high]).start;
      IF notBefore <= lval THEN {
        cm.commentTable.lastx ← lastx;
        RETURN [cm.commentTable.last ← last]}};
    RETURN [NIL]};

  TestBreakHint: PUBLIC PROC[cm: CommentM, start: Index, next: Index] RETURNS[BOOL] ~ {
    IF start = 0 OR next <= start THEN RETURN [TRUE];
    IF cm.breakTable = NIL OR cm.breakTable.size <= 1 THEN RETURN [FALSE];
     {low: NAT ← 0;
      high: NAT ← cm.breakTable.size - 1;
      DO
	last: NAT ← (low+high)/2;
	lval: Index ← cm.breakTable[last];
	IF low = high THEN RETURN [start < lval AND next > lval];
	IF start < lval THEN {
	  IF next > lval THEN RETURN [TRUE];
	  high ← last;
	  LOOP};
	IF start >= lval THEN {low ← last+1; LOOP};
	ENDLOOP;
     }};

  Explode: PUBLIC PROC[ref: Ref] RETURNS[start: Index, text: Text, lastToken, prefix: Index] ~ {
    IF ref = NIL THEN RETURN [Index.LAST, NIL, Index.LAST, 0];
    RETURN [ref.start, ref.text, ref.lastToken, ref.prefix]};

  SetEnding: PUBLIC PROC[cm: CommentM, end: Index] ~ {cm.ending ← end};

  GetEnding: PUBLIC PROC[cm: CommentM] RETURNS[Index] ~ {RETURN [cm.ending]};

  }.