-- File: JDSmoduleImpl.mesa 
-- last edit by: 
--   Nagata, April 7, 1982; converted to Trinity 
--   Glenn, September 10, 1981  5:56 PM 
-- Changed the Kanji convert of GET to ignore JDSBOR char in that field 9/10/81
-- Added PhonicToJDS procedure to this module 7/23/81
-- Changed the CATcode default value from: 22B to: 26B (4 new Catcodes)

DIRECTORY
  CharDefs USING [Char],
  Environment USING [Block],
  Heap USING [FreeNode, MakeNode, systemZone],
  JDSmoduleDefs,
  JDSPosDefs USING [GetCatValue, GetPosValue],
  JDStoOISDefs USING [JDStoOIS, lastJDSKanji, UNDF],
  MStream USING [EndOf],
  OISDictDataDefs USING [Entry],
  Stream USING [Block, Byte, CompletionCode, defaultObject, GetByte, GetPosition,
    GetWord, Handle, InputOptions, Object, PutBlock, PutByte, SendAttention,
    SetPosition, SetSST, SubSequenceType, WaitForAttention, Word];

JDSmoduleImpl: MONITOR IMPORTS Heap, JDSPosDefs, JDStoOISDefs, MStream, Stream
                       EXPORTS JDSmoduleDefs =
  BEGIN 

  -- TYPEs

  streamZone: UNCOUNTED ZONE = Heap.systemZone;
  Handle: TYPE = Stream.Handle;

  BoxObject: TYPE = RECORD [base: LONG CARDINAL, bytecount: CARDINAL];
  BoxPtr: TYPE = LONG POINTER TO ARRAY [0..0) OF BoxObject;
  Box: BoxPtr ← NIL;

  jdsStreamObject: TYPE = MACHINE DEPENDENT RECORD [
    defaultObject: Stream.Object, clientSH: Stream.Handle, jdsPtr: jdsFileObject];

  StreamJDSObject: TYPE = LONG POINTER TO jdsStreamObject;

  jdsObject: TYPE = RECORD [
    jdspage: ARRAY [0..30] OF CARDINAL,  --  page directory of JDS file
    numofboxes: CARDINAL ← 0,  --  count of number of boxes in page
    wordsperbox: CARDINAL ← 0,  --  count of words per box (+ tabsets)
    wordcount: CARDINAL ← 0,  --  count of total text words in page
    wordsread: CARDINAL ← 0,  --  count of total words read so far
    textwordsread: CARDINAL ← 0,  --  count of total text chars read so far
    pageno: CARDINAL ← 0,  --  index to JDS.jdspage array
    boxno: CARDINAL ← 0,  --  index to BoxObjects
    boxtextbytesread: CARDINAL ← 0,  --  count of text bytes read from box
    recordsmade: CARDINAL ← 0,  --  count of records (16 words) built/read *
    wurd: CARDINAL ← 0,  --  JDS text character (in sequence)
    curpage: LONG CARDINAL ← 0,  --  current page index
    curbyteindx: LONG CARDINAL ← 0,  --  current stream byte index
    topoftext: LONG CARDINAL ← 0,  --  Starting index of JDS text in file
    pagetextstart: LONG CARDINAL ← 0,  --  current page start of text index
    startofpage: BOOLEAN ← TRUE,  --  flag page change
    startofbox: BOOLEAN ← TRUE];  --  flag box change

  jdsFileObject: TYPE = LONG POINTER TO jdsObject;

  -- Constant declarations

  maxjdspages: CARDINAL = 30;
  jdspagesize: CARDINAL = 16;
  boxOffset: LONG CARDINAL = 22;
  jdsfilecheckword: CARDINAL = 12345B;
  JDSEOR: CARDINAL = 10000B;
  JDSBOR: CARDINAL = 46B;
  JDSBLK: CARDINAL = 375B;
  NULLCHAR: CharDefs.Char = [ 177B, 377B];
  UNDFCHAR: CharDefs.Char = [0, 201B];
  -- PUBLIC SIGNALS 

  endOfJDS: PUBLIC SIGNAL = CODE;

  -- PUBLIC PROCEDURES  

  create: PUBLIC PROCEDURE [h: Handle] RETURNS [hJDS: Handle] =
    BEGIN
    i: CARDINAL;
    sH: StreamJDSObject;
    sH ← streamZone.NEW[
      jdsStreamObject ← [
      defaultObject: Stream.defaultObject, clientSH: h, jdsPtr: NIL]];
    sH.defaultObject.options ← [TRUE, FALSE, FALSE, FALSE, FALSE, TRUE];
    sH.defaultObject.getByte ← getByte;
    sH.defaultObject.putByte ← putByte;
    sH.defaultObject.getWord ← getWord;  --  plug in my own proc
    sH.defaultObject.get ← get;  --  plug in my own proc
    sH.defaultObject.put ← put;
    sH.defaultObject.delete ← delete;
    sH.defaultObject.setSST ← setSST;
    sH.defaultObject.sendAttention ← sendAttention;
    sH.defaultObject.waitAttention ← waitAttention;
    sH.clientSH ← h;
    sH.jdsPtr ← streamZone.NEW[jdsObject];
    Stream.SetPosition[h, 0];
    IF MStream.EndOf[h] THEN ERROR;
    FOR i IN [0..256) DO
      sH.jdsPtr.wurd ← Stream.GetWord[h];
      IF i <= maxjdspages THEN sH.jdsPtr.jdspage[i] ← sH.jdsPtr.wurd;
      sH.jdsPtr.wordsread ← sH.jdsPtr.wordsread + 1;
      ENDLOOP;
    IF sH.jdsPtr.jdspage[0] # jdsfilecheckword THEN ERROR;  -- not graceful !
    sH.jdsPtr.startofpage ← TRUE;
    sH.jdsPtr.startofbox ← TRUE;
    sH.jdsPtr.pageno ← 0;
    sH.jdsPtr.curpage ← 0;
    sH.jdsPtr.curbyteindx ← Stream.GetPosition[h];
    sH.jdsPtr.topoftext ← sH.jdsPtr.curbyteindx;
    sH.jdsPtr.recordsmade ← 0;
    RETURN[LOOPHOLE[sH, Handle]];
    END;

  topOfJDS: PUBLIC PROCEDURE [sH: Stream.Handle] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    sh.jdsPtr.startofpage ← TRUE;
    sh.jdsPtr.startofbox ← TRUE;
    sh.jdsPtr.pageno ← 0;
    sh.jdsPtr.curpage ← 0;
    sh.jdsPtr.recordsmade ← 0;
    sh.jdsPtr.curbyteindx ← sh.jdsPtr.topoftext;
    Stream.SetPosition[sh.clientSH, sh.jdsPtr.topoftext];
    END;

  getByte: PROCEDURE [sH: Stream.Handle] RETURNS [byte: Stream.Byte] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    RETURN[Stream.GetByte[sh.clientSH]];
    END;

  putByte: PROCEDURE [sH: Stream.Handle, byte: Stream.Byte] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    Stream.PutByte[sh.clientSH, byte];
    END;

  put: PROCEDURE [
    sH: Stream.Handle, block: Stream.Block, endRecord: BOOLEAN] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    Stream.PutBlock[sh.clientSH, block, endRecord];
    END;

  get: PROCEDURE [
    sH: Stream.Handle, block: Stream.Block, options: Stream.InputOptions]
    RETURNS [
      bytesTransferred: CARDINAL, why: Stream.CompletionCode, 
      sst: Stream.SubSequenceType] =
    BEGIN  -- Reads a JDS file and creates a formatted Master Entry/record.
    wurd: CARDINAL;
    i, oiscode: CARDINAL ← 0;
    tempindex: LONG CARDINAL;
    done: BOOLEAN ← FALSE;
    newrec: BOOLEAN ← FALSE;
    eor: BOOLEAN ← FALSE;
    sh: StreamJDSObject ← LOOPHOLE[sH];
    JDS: jdsFileObject ← sh.jdsPtr;
    RP: LONG POINTER TO OISDictDataDefs.Entry ← LOOPHOLE[block.blockPointer];
    BEGIN  -- Dummy Block to contain the Exit clause
    ENABLE endOfJDS => BEGIN why ← endOfStream; GOTO fini; END;
    IF (block.stopIndexPlusOne MOD 2) # 0 THEN ERROR;  -- using words NOT bytes
    bytesTransferred ← 0;
    -- Conditional needed for Show Top flag ...reset to JDS.topoftext 
    Stream.SetPosition[sh.clientSH, JDS.curbyteindx];

    -- get Start of JDS record (".")
    UNTIL JDS.wurd = JDSBOR DO JDS.wurd ← getWord[sH]; ENDLOOP;
    -- Initialize record
    RP↑.notdel ← TRUE;
    RP↑.notMarked ← TRUE;
    RP↑.kanjiCodeSet ← OIS;
    RP↑.kanaCodeSet ← PhonicCodes;
    RP↑.logicalDict ← 0;
    -- get KANA chars with 377B-fill (minus 1) 
    FOR i IN [0..13] DO
      IF done THEN RP↑.kana[i] ← 377B
      ELSE
        BEGIN
        JDS.wurd ← getWord[sH];
        IF JDS.wurd = JDSEOR THEN {
          eor ← newrec ← done ← TRUE; RP↑.kana[i] ← 377B; }
        ELSE
          -- IF JDS.wurd = JDSBOR THEN {newrec ← done ← TRUE; RP↑.kana[i] ← 377B;} ELSE
          IF JDS.wurd = JDSBLK THEN {done ← TRUE; RP↑.kana[i] ← 377B; }
          ELSE RP↑.kana[i] ← JDSToPhonic[JDS.wurd];
        END;
      ENDLOOP;
    done ← FALSE;
    i ← 0;
    IF newrec THEN GOTO dunatkanji;
    -- get Kanji chars with 177777B-fill (minus 1)
    UNTIL JDS.wurd # JDSBLK DO JDS.wurd ← getWord[sH]; ENDLOOP;
    FOR i IN [0..6] DO
      IF done THEN RP↑.kanji[i] ← NULLCHAR
      ELSE
        BEGIN
        IF JDS.wurd = JDSEOR THEN {
          RP↑.kanji[i] ← NULLCHAR; eor ← newrec ← done ← TRUE; }
        ELSE
          IF JDS.wurd = JDSBLK THEN {RP↑.kanji[i] ← NULLCHAR; done ← TRUE; }
          ELSE {
	    IF JDS.wurd IN [1..JDStoOISDefs.lastJDSKanji] THEN
	     { oiscode ← JDStoOISDefs.JDStoOIS[JDS.wurd];
	     IF oiscode = JDStoOISDefs.UNDF THEN
	        RP↑.kanji[i] ← UNDFCHAR
		ELSE RP↑.kanji[i] ← LOOPHOLE[oiscode, CharDefs.Char];} 
	    ELSE RP↑.kanji[i] ← UNDFCHAR; };
        IF NOT done THEN JDS.wurd ← getWord[sH];
        END;
      ENDLOOP;
    done ← FALSE;
    i ← 0;
    IF newrec THEN GOTO dunatfreq;
    -- get Frequency code value from character  (0 - 9, default = 0) 
    UNTIL JDS.wurd # JDSBLK DO wurd ← getWord[sH]; ENDLOOP;
    IF JDS.wurd = JDSEOR THEN {eor ← TRUE; GOTO dunatfreq; }
    ELSE
      IF JDS.wurd = JDSBOR THEN GOTO dunatfreq
      ELSE RP↑.freq ← IF JDS.wurd IN [216B..227B] THEN JDS.wurd - 216B ELSE 0;
    -- get Part Of Speech code value from characters (0 - 244, default = 0)
    wurd ← getWord[sH];  -- get the next word(s)
    UNTIL JDS.wurd # JDSBLK DO wurd ← getWord[sH]; ENDLOOP;
    IF JDS.wurd = JDSEOR THEN {eor ← TRUE; GOTO dunatpos; }
    ELSE
      -- IF JDS.wurd = JDSBOR THEN GOTO dunatpos ELSE
      RP↑.pos ← JDSPosDefs.GetPosValue[wurd, getWord[sH]];
    --  get Catagory code value from character (0 - 25B, default = 26B)
    wurd ← getWord[sH];  -- get the next word(s)
    UNTIL JDS.wurd # JDSBLK DO JDS.wurd ← getWord[sH]; ENDLOOP;
    IF JDS.wurd = JDSEOR THEN {eor ← TRUE; GOTO dunatcat; }
    ELSE
      IF JDS.wurd = JDSBOR THEN GOTO dunatcat
      ELSE RP↑.cat ← JDSPosDefs.GetCatValue[JDS.wurd];

    --  get End of JDS record (CR)
    done ← FALSE;
    i ← 0;
    IF NOT eor THEN
      UNTIL JDS.wurd = JDSEOR DO
        BEGIN
        JDS.wurd ← getWord[sH];
        IF JDS.wurd = JDSBOR THEN  -- No CR for this record...reset;
          BEGIN
          tempindex ← Stream.GetPosition[sh.clientSH];
          tempindex ← tempindex - 2;
          Stream.SetPosition[sh.clientSH, tempindex];
          GOTO fini;
          END;
        END;
        ENDLOOP;

    GOTO fini;

    -- fills the rest of the incomplete record with default values
    EXITS  -- flagging the last unused byte with a "?" (6)
      dunatkanji =>
        BEGIN
        IF eor THEN  -- No CR for this record...reset;
          BEGIN
          tempindex ← Stream.GetPosition[sh.clientSH];
          tempindex ← tempindex - 2;
          Stream.SetPosition[sh.clientSH, tempindex];
          END;
        FOR i IN [0..6] DO RP↑.kanji[i] ← NULLCHAR; ENDLOOP;
        RP↑.freq ← RP↑.pos ← 0;
        RP↑.cat ← 26B;
        JDS.recordsmade ← JDS.recordsmade + 1;
        bytesTransferred ← bytesTransferred + 32;
        END;
      dunatfreq =>
        BEGIN
        IF eor THEN  -- No CR for this record...reset;
          BEGIN
          tempindex ← Stream.GetPosition[sh.clientSH];
          tempindex ← tempindex - 2;
          Stream.SetPosition[sh.clientSH, tempindex];
          END;
        RP↑.freq ← RP↑.pos ← 0;
        RP↑.cat ← 26B;
        JDS.recordsmade ← JDS.recordsmade + 1;
        bytesTransferred ← bytesTransferred + 32;
        END;
      dunatpos =>
        BEGIN
        IF eor THEN  -- No CR for this record...reset;
          BEGIN
          tempindex ← Stream.GetPosition[sh.clientSH];
          tempindex ← tempindex - 2;
          Stream.SetPosition[sh.clientSH, tempindex];
          END;
        RP↑.pos ← 0;
        RP↑.cat ← 26B;
        JDS.recordsmade ← JDS.recordsmade + 1;
        bytesTransferred ← bytesTransferred + 32;
        END;
      dunatcat =>
        BEGIN
        RP↑.cat ← 26B;
        JDS.recordsmade ← JDS.recordsmade + 1;
        bytesTransferred ← bytesTransferred + 32;
        END;
      fini =>
        BEGIN
        JDS.recordsmade ← JDS.recordsmade + 1;
        bytesTransferred ← bytesTransferred + 32;
        END;
    END;  -- Dummy Block to contain the EXITS clause
    IF MStream.EndOf[sh.clientSH] THEN why ← endOfStream
    ELSE JDS.curbyteindx ← Stream.GetPosition[sh.clientSH];
    RETURN[bytesTransferred, why, sst];
    END;

  getWord: PRIVATE PROCEDURE [sH: Stream.Handle] RETURNS [word: Stream.Word] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    JDS: jdsFileObject ← sh.jdsPtr;
    IF JDS.startofpage THEN
      BEGIN
      JDS.pageno ← JDS.pageno + 1;
      IF JDS.pageno > maxjdspages THEN SIGNAL endOfJDS
      ELSE IF JDS.jdspage[JDS.pageno] = 0 THEN RETURN[getWord[sH]];
      JDS.curpage ← LONG[512]*LONG[JDS.jdspage[JDS.pageno]];
      IF JDS.textwordsread > 0 THEN Heap.FreeNode[p: Box];  -- Flush old boxes
      Stream.SetPosition[sh.clientSH, JDS.curpage];  -- Start of page
      JDS.numofboxes ← Stream.GetWord[sh.clientSH];
      JDS.wordsperbox ← Stream.GetWord[sh.clientSH];
      JDS.wordsperbox ← JDS.wordsperbox + Stream.GetWord[sh.clientSH];
      JDS.curbyteindx ← JDS.curpage + 6;  -- update byte stream indx
      Box ← AllocateBoxes[JDS];
      LoadBoxPtrs[sh.clientSH, JDS];
      JDS.wordcount ← Stream.GetWord[sh.clientSH];  -- Total Text wordcount of Page
      JDS.curbyteindx ← JDS.curbyteindx + 2;  -- set to start page text (bytes)
      JDS.pagetextstart ← JDS.curbyteindx;  -- set to start page text (bytes)
      JDS.wordsread ← 4 + (JDS.numofboxes*JDS.wordsperbox);
      JDS.textwordsread ← 0;
      JDS.boxno ← 0;
      JDS.startofpage ← FALSE;
      JDS.startofbox ← TRUE;
      RETURN[getWord[sH]];
      END
    ELSE

      IF JDS.startofbox THEN
        BEGIN
        IF JDS.boxno >= JDS.numofboxes THEN
          BEGIN JDS.startofpage ← TRUE; RETURN[getWord[sH]]; END;
        JDS.curbyteindx ← JDS.pagetextstart + Box[JDS.boxno].base;
        Stream.SetPosition[sh.clientSH, JDS.curbyteindx];
        JDS.boxtextbytesread ← 0;
        JDS.startofbox ← FALSE;
        RETURN[getWord[sH]];
        END
      ELSE

        BEGIN
        DO

          IF JDS.wordsread >= 256*16 THEN  -- End of Page ?
            BEGIN JDS.startofpage ← TRUE; RETURN[getWord[sH]]; END;

          IF JDS.textwordsread >= JDS.wordcount THEN  -- End of Page Text ?
            BEGIN JDS.startofpage ← TRUE; RETURN[getWord[sH]]; END;

          IF JDS.boxtextbytesread >= Box[JDS.boxno].bytecount THEN  -- End of Box Text ?
            BEGIN
            JDS.startofbox ← TRUE;
            JDS.boxno ← JDS.boxno + 1;
            RETURN[getWord[sH]];
            END;

          JDS.wurd ← Stream.GetWord[sh.clientSH];  -- Get JDS word
          JDS.wordsread ← JDS.wordsread + 1;
          JDS.textwordsread ← JDS.textwordsread + 1;
          JDS.boxtextbytesread ← JDS.boxtextbytesread + 2;

          IF JDS.wurd IN [1..6624B] OR JDS.wurd = 10000B THEN RETURN[JDS.wurd]
          ELSE RETURN[JDS.wurd ← 201B];

          ENDLOOP;
        END;
    END;

  delete: PROCEDURE [sH: Stream.Handle] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    streamZone.FREE[@sh.jdsPtr];
    streamZone.FREE[@sh];
    END;

  setSST: PROCEDURE [sH: Stream.Handle, sst: Stream.SubSequenceType] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    Stream.SetSST[sh.clientSH, sst];
    END;

  sendAttention: PROCEDURE [sH: Stream.Handle, byte: Stream.Byte] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    Stream.SendAttention[sh.clientSH, byte];
    END;

  waitAttention: PROCEDURE [sH: Stream.Handle] RETURNS [byte: Stream.Byte] =
    BEGIN
    sh: StreamJDSObject ← LOOPHOLE[sH];
    RETURN[Stream.WaitForAttention[sh.clientSH]];
    END;

  AllocateBoxes: PRIVATE PROCEDURE [JDS: jdsFileObject] RETURNS [p: BoxPtr] =
    BEGIN  -- Allocates space for Box records and initiates 
    i: CARDINAL;
    p ← Heap.MakeNode[n: SIZE[BoxObject]*JDS.numofboxes];
    FOR i IN [0..JDS.numofboxes) DO p[i].base ← 0; p[i].bytecount ← 0; ENDLOOP;
    RETURN[p];
    END;

  LoadBoxPtrs: PRIVATE PROCEDURE [sH: Stream.Handle, JDS: jdsFileObject] =
    BEGIN  --  Reads in the Box Text pointers and counts 
    i: CARDINAL;
    bytesperbox, indx: LONG CARDINAL;
    bytesperbox ← JDS.wordsperbox*2;
    IF MStream.EndOf[sH] THEN ERROR;
    indx ← JDS.curbyteindx + boxOffset;
    FOR i IN [0..JDS.numofboxes) DO
      Stream.SetPosition[sH, indx];  -- 12th word of box  
      Box[i].base ← Stream.GetWord[sH];
      Box[i].bytecount ← Stream.GetWord[sH];
      indx ← indx + bytesperbox;
      ENDLOOP;
    JDS.curbyteindx ← JDS.curbyteindx + (bytesperbox*JDS.numofboxes);
    Stream.SetPosition[sH, JDS.curbyteindx];  -- get total text wordcount
    END;

  JDSToPhonic: PROC [JDSCode: CARDINAL] RETURNS [Phonic: [0..377B]] =
    BEGIN
    Phonic ←
      SELECT JDSCode FROM
        IN [400B..523B] => JDSCode - 400B,  --  kana
        IN [216B..227B] => JDSCode - 36B,  --  digits
        IN [230B..261B] => JDSCode - 7B,  --  UpperCase Romaji
        IN [262B..313B] => JDSCode - 21B,  --  lowercase Romaji
        = 41B => 377B,  --  blank
        = 42B, =44 => 131B,  --  comma
        = 43B, =45B => 132B,  --  period
        = 157B => 321B,  --  yen sign
        = 160B => 144B,  --  dollar sign
        ENDCASE => 152B;  --  asterix default
    END;



  END.