-- file:  BruceTalker.mesa
-- edited by Barth, March 26, 1982  1:41 PM
-- edited by Chi Yung Fu, April 30, 1982  2:40 PM
-- edited by Chi Yung Fu, September 1, 1983  11:28 AM
-- edited by JWhite, 22-Dec-83  9:34:50

DIRECTORY
  BruceDefs,
  csD: FROM "CoreStreamDefs",
  dsD: FROM "DisplayDefs",
  Editor,
  exD: FROM "ExceptionDefs",
  InlineDefs,
  inD: FROM "InteractorDefs",
  intCommon,
  lmD: FROM "LaurelMenuDefs",
  MachineParseDefs,
  MiscDefs,
  Process,
  prD: FROM "ProtectionDefs",
  Storage,
  String,
  vmD: FROM "VirtualMgrDefs",
  VMDefs;

BruceTalker: PROGRAM
  IMPORTS BruceDefs, csD, Editor, exD, InlineDefs,
          inD, intC: intCommon, lmD, MachineParseDefs, MiscDefs,
          Process, prD, Storage,
          String, vmD, VMDefs
  EXPORTS BruceDefs =
BEGIN

running: BOOLEAN ← TRUE;

bufferIndex: TYPE = [0..SIZE[BruceDefs.Recipe]);
bufferPtr: TYPE = POINTER TO ARRAY bufferIndex OF WORD;
bruceRecipe: BruceDefs.RecipePtr;
bruceDefault: BruceDefs.RecipePtr;
oldCmHp: inD.HousePtr;
LowerHp: inD.HousePtr;
TubeTextHp: inD.HousePtr;


StartTalker: PUBLIC PROCEDURE=
BEGIN
sh:csD.StreamHandle;
bp:bufferPtr;
cm: inD.MessageTextNbrPtr = intC.cmTextNbr;
LowerMenu: ARRAY [0 .. 12) OF lmD.HouseDescriptor ←
  [[text: "Start"L, command: StartCommand],
  [text: "Abort"L, command: AbortCommand],
  [text: "AckAlarm"L, command: AckAlarmCommand],
  [text: "Get"L, type: brackets, bracketsText: ""L, command: GetCommand],
  [text: "Put"L, type: brackets, bracketsText: ""L, command: PutCommand],
  [text: "Tube"L, type: brackets, bracketsText: " "L, command: TubeCommand],
  [text: "Upload"L, command: UploadCommand],
  [text: "Download"L, command: DownloadCommand],
  [text: "Quit"L, command: BruceQuitCommand, indicDone: FALSE],
  [text: "Compile"L, command: CompileCommand],
  [text: "InitTables"L, command: InitTablesCommand],
  [text: "UpdateDefault"L, command: UpdateDefault]];

LowerHp ← lmD.CreateCommandHouses
  [DESCRIPTOR[LowerMenu], Storage.Node, Storage.String];
TubeTextHp ← lmD.MapHouseTextToHousePtr[LowerHp, "Tube"L].nextHouse;
bruceDefault ← Storage.Node[SIZE[BruceDefs.Recipe]];
bp ← LOOPHOLE[bruceDefault];
sh ← csD.OpenFromName["BruceDefaultRecipe.binary"L, word, read
  ! VMDefs.CantOpen => GOTO defaultNotFound];
FOR i:CARDINAL IN bufferIndex DO
  bp[i] ← csD.Read[sh ! VMDefs.Error => GOTO earlyEnd];
ENDLOOP;
csD.Destroy[sh];
Editor.Getter[0, vmD.GetMessageSize[cm.message], cm, "BruceConstants.text"L];
BruceDefs.InitTables[cm.message
    ! MachineParseDefs.ParseFault => BEGIN
      ReportParseFault[why, pfBegin, pfEnd];
      GOTO oops;
    END];
EXITS
  defaultNotFound => exD.DisplayExceptionString[
    "Couldn't find file BruceDefaultRecipe.binary"L];
  earlyEnd => exD.DisplayExceptionString[
    "File BruceDefaultRecipe.binary is messed up"L];
  oops => NULL;
END; -- of StartTalker


InstallTalker: PUBLIC PROCEDURE =
BEGIN
bruceRecipe ← Storage.Node[SIZE[BruceDefs.Recipe]];
MiscDefs.Zero[bruceRecipe,SIZE[BruceDefs.Recipe]];
inD.StopBlinkingCaret[];
oldCmHp ← intC.cmCommandNbr.houses;
intC.cmCommandNbr.houses ← LowerHp;
inD.ChangeCommandMenu
  [cnp: intC.cmCommandNbr, region: intC.CMCommandRegion, linesToKeep: 0];
IF inD.CaretIsBlinking[] THEN inD.SetCaretBlinking[intC.target.point, intC.target.mnp];
BruceDefs.Initialize[];
END;  -- of InstallTalker --

ExecuteStep: PUBLIC PROCEDURE [data, spec: vmD.VirtualMessagePtr]
  RETURNS [result: STRING] =
BEGIN
  tube:BruceDefs.TubeNumber;
  InlineDefs.COPY[bruceDefault, SIZE[BruceDefs.Recipe], bruceRecipe];
  tube ← BruceDefs.SpecToBinary[data, spec, bruceRecipe
    ! MachineParseDefs.ParseFault => BEGIN
      ReportParseFault[why, pfBegin, pfEnd];
      GOTO oops;
    END];
  BruceDefs.DownLoad[tube, bruceRecipe
    !BruceDefs.CommFailure => {ReportCommFailure[]; GOTO oops}];
  BruceDefs.Command[tube, Start
    !BruceDefs.CommFailure => {ReportCommFailure[]; GOTO oops}];
EXITS
  oops => NULL;
END;  -- of ExecuteStep --

GetDefault: PUBLIC PROCEDURE[brp: BruceDefs.RecipePtr] =
BEGIN
InlineDefs.COPY[bruceDefault, SIZE[BruceDefs.Recipe], brp];
END;  -- of GetDefault --

UpdateDefault: inD.CommandProcedure =
BEGIN
sh:csD.StreamHandle;
bp:bufferPtr;
bp ← LOOPHOLE[bruceDefault];

--get confirmation before updating default receipe
exD.DisplayBothExceptionLines
["Do you really want to update the default receipe.",0, "", 0, FALSE];
IF  ~inD.Confirm[2] THEN GOTO oops;
exD.DisplayBothExceptionLines
["LAST CHANCE!  Do you really want to do this? Can't claim computer failure.",
0, "", 0, TRUE];
IF  ~inD.Confirm[2] THEN GOTO oops;

--update default receipe
InlineDefs.COPY[bruceRecipe, SIZE[BruceDefs.Recipe], bruceDefault];
sh ← csD.OpenFromName["BruceDefaultRecipe.binary"L, word,
  overwrite ! VMDefs.CantOpen => GOTO streamFailure];
FOR i:CARDINAL IN bufferIndex DO
  csD.Write[sh, bp[i] ! VMDefs.Error => GOTO streamFailure];
ENDLOOP;
csD.Close[sh! VMDefs.Error => GOTO streamFailure];
EXITS
  oops => NULL;
  streamFailure => exD.DisplayExceptionString[
    "Couldn't write BruceDefaultRecipe.binary"L];
END;  -- of UpdateDefault --


BruceQuitCommand: inD.CommandProcedure =
BEGIN
Storage.Free[bruceRecipe];
inD.StopBlinkingCaret[];
intC.cmCommandNbr.houses ← oldCmHp;
inD.ChangeCommandMenu
  [cnp: intC.cmCommandNbr, region: intC.CMCommandRegion, linesToKeep: 0];
IF inD.CaretIsBlinking[] THEN inD.SetCaretBlinking[intC.target.point, intC.target.mnp];
BruceDefs.Terminate[];
running ← FALSE;
END;  -- of BruceQuitCommand --


GetCommand: inD.CommandProcedure =
BEGIN
cm: inD.MessageTextNbrPtr = intC.cmTextNbr;
oldText: STRING ← [inD.maxBracketStringLength];
String.AppendString[oldText, hp.nextHouse.text];
IF ~confirmed AND ~inD.ConfirmBrackets[hp: hp.nextHouse, fileExtension: ".form."L]
  THEN RETURN;
IF intC.composedMessageEdited AND vmD.GetMessageSize[cm.message] # 0
  AND ~inD.AskUserToConfirm[exD.willReplaceMessage]
  THEN {inD.RefreshHouse[hp.nextHouse, oldText]; RETURN};
Editor.Getter[0, vmD.GetMessageSize[cm.message], cm, hp.nextHouse.text];
intC.runCommandMode ← FALSE;
END;  -- of GetCommand --


PutCommand: inD.CommandProcedure =
BEGIN
oldText: STRING ← [inD.maxBracketStringLength];
String.AppendString[oldText, hp.nextHouse.text];
IF (confirmed OR inD.ConfirmBrackets[hp: hp.nextHouse,
  fileExtension: ".form."L])
  AND Editor.Putter[0, vmD.GetMessageSize[intC.cmTextNbr.message],
  intC.cmTextNbr, hp.nextHouse, oldText]
  THEN intC.composedMessageEdited ← FALSE;
IF inD.CaretIsBlinking[] THEN inD.SetCaretBlinking[intC.target.point,
  intC.target.mnp];
END;  -- of PutCommand --


TubeCommand: inD.CommandProcedure =
BEGIN
  oldText: STRING ← [inD.maxBracketStringLength];
  String.AppendString[oldText, hp.nextHouse.text];
  IF ~confirmed AND ~inD.ConfirmBrackets[hp: hp.nextHouse] THEN RETURN;
  IF hp.nextHouse.text.length#1 OR LOOPHOLE[hp.nextHouse.text[0],CARDINAL] <
    LOOPHOLE['1,CARDINAL] OR LOOPHOLE[hp.nextHouse.text[0],CARDINAL] >
    LOOPHOLE['8,CARDINAL] THEN BEGIN
    exD.DisplayExceptionString["Invalid tube number"L];
    inD.RefreshHouse[hp.nextHouse, oldText];
    RETURN;
  END;
END;  -- of TubeCommand --


GetTubeNumber: PROCEDURE RETURNS[tube:BruceDefs.TubeNumber]=
BEGIN
  tube ← LOOPHOLE[TubeTextHp.text[0], CARDINAL] - LOOPHOLE['0, CARDINAL];
END; -- of GetTubeNumber

ReportCommFailure: PROCEDURE=
BEGIN
exD.DisplayExceptionString["Communications failure, try again."L];
END; -- of ReportCommFailure


StartCommand: inD.CommandProcedure =
BEGIN
  exD.DisplayBothExceptionLines
  ["Is the tube number correct?  I hope you human know what you are doing.",0, "", 0, FALSE];
  IF  ~inD.Confirm[2] THEN GOTO oops;
  exD.DisplayBothExceptionLines
  ["FINAL CALL!  Otherwise, wave goodbye to the wafers.",
  0, "", 0, TRUE];
  IF  ~inD.Confirm[2] THEN GOTO oops;

  BruceDefs.Command[GetTubeNumber[], Start
    !BruceDefs.CommFailure => {ReportCommFailure[]; GOTO oops}];
EXITS
  oops => NULL;
END;  -- of StartCommand --


AbortCommand: inD.CommandProcedure =
BEGIN
  BruceDefs.Command[GetTubeNumber[], Abort
    !BruceDefs.CommFailure => {ReportCommFailure[]; GOTO oops}];
EXITS
  oops => NULL;
END;  -- of AbortCommand --


AckAlarmCommand: inD.CommandProcedure =
BEGIN
  BruceDefs.Command[GetTubeNumber[], AckAlarm
    !BruceDefs.CommFailure => {ReportCommFailure[]; GOTO oops}];
EXITS
  oops => NULL;
END;  -- of AckAlarmCommand --


UploadCommand: inD.CommandProcedure =
BEGIN
  mnp: inD.MessageTextNbrPtr = intC.cmTextNbr;
  composedMessage: vmD.ComposedMessagePtr;
  IF ~confirmed AND intC.composedMessageEdited
    AND vmD.GetMessageSize[mnp.message] # 0
    AND ~inD.AskUserToConfirm[exD.willReplaceMessage]
    THEN RETURN;
  BruceDefs.UpLoad[GetTubeNumber[], bruceRecipe
    !BruceDefs.CommFailure => {ReportCommFailure[]; GOTO oops}];
  Editor.SwapMessageWithDeletionBuffer[mnp];
  Editor.ResetInsertionBuffer[mnp];
  composedMessage ← vmD.ComposedMessage[mnp.message];
  vmD.InitComposedMessage[composedMessage, ""L];
  BruceDefs.BinaryToAscii[composedMessage, bruceRecipe];
  intC.commandType ← get;
  intC.source ← inD.TextSelection
    [mnp: mnp, start: 0, end: 0, point: 0, mode: char, pendingDelete: FALSE, key: 0];
  intC.target ← inD.TextSelection
    [mnp: mnp, start: 0, end: 0, point: 0, mode: char, pendingDelete: FALSE, key: 0];
  intC.newTargetSelection ← TRUE;
  intC.actionPoint ← 0;
  intC.pendingDeleteSetByControl ← FALSE;
  IF mnp.protectedFieldPtr # NIL THEN
    prD.UnprotectAllFields[@mnp.protectedFieldPtr];
  Editor.RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: mnp.lines,
    mnp: mnp];
  mnp.haveMessage ← TRUE;
  intC.runCommandMode ← FALSE;
  intC.composedMessageEdited ← FALSE;
EXITS
  oops => NULL;
END;  -- of UploadCommand --


DownloadCommand: inD.CommandProcedure =
BEGIN
  exD.DisplayBothExceptionLines
  ["Tube number correct? Download into a wrong tube could be DISASTROUS.",0, "", 0, FALSE];
  IF  ~inD.Confirm[2] THEN GOTO oops;
  exD.DisplayBothExceptionLines
  ["LAST CHANCE!  Uncle Bill may get you for this.  Just don't blame it on me.",
  0, "", 0, TRUE];
  IF  ~inD.Confirm[2] THEN GOTO oops;

InlineDefs.COPY[bruceDefault, SIZE[BruceDefs.Recipe], bruceRecipe];
  BruceDefs.AsciiToBinary[intC.cmTextNbr.message, bruceRecipe
    ! MachineParseDefs.ParseFault => BEGIN
      ReportParseFault[why, pfBegin, pfEnd];
      GOTO oops;
    END];
  BruceDefs.DownLoad[GetTubeNumber[], bruceRecipe
    !BruceDefs.CommFailure => {ReportCommFailure[]; GOTO oops}];
EXITS
  oops => NULL;
END;  -- of DownloadCommand --

CompileCommand: inD.CommandProcedure =
BEGIN
  mnp: inD.MessageTextNbrPtr = intC.cmTextNbr;
  composedMessage: vmD.ComposedMessagePtr;
  tube:BruceDefs.TubeNumber;
  composedMessage ← vmD.ComposedMessage[mnp.message];
  InlineDefs.COPY[bruceDefault, SIZE[BruceDefs.Recipe], bruceRecipe];
  tube ← BruceDefs.SpecToBinary[NIL, mnp.message, bruceRecipe
    ! MachineParseDefs.ParseFault => BEGIN
      ReportParseFault[why, pfBegin, pfEnd];
      GOTO oops;
    END];
  BruceDefs.BinaryToAscii[composedMessage, bruceRecipe];
  Editor.RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: mnp.lines,
    mnp: mnp];
EXITS
  oops => NULL;
END;  -- of CompileCommand --

InitTablesCommand: inD.CommandProcedure =
BEGIN
  mnp: inD.MessageTextNbrPtr = intC.cmTextNbr;
  composedMessage: vmD.ComposedMessagePtr;
  composedMessage ← vmD.ComposedMessage[mnp.message];
  
  exD.DisplayBothExceptionLines
  ["Are you sure you want InitTable.",0, "", 0, FALSE];
  IF  ~inD.Confirm[2] THEN GOTO oops;
  exD.DisplayBothExceptionLines
  ["LAST CHANCE. Are you ABSOLUTELY sure you want InitTable.",
  0, "", 0, TRUE];
  IF  ~inD.Confirm[2] THEN GOTO oops;

  BruceDefs.InitTables[composedMessage
    ! MachineParseDefs.ParseFault => BEGIN
      ReportParseFault[why, pfBegin, pfEnd];
      GOTO oops;
    END];
EXITS
  oops => NULL;
END;  -- of CompileCommand --

ReportParseFault: PROCEDURE[why:STRING, pfBegin:vmD.CharIndex,
  pfEnd:vmD.CharIndex] =
BEGIN
  BruceDefs.WarningCarat[pfBegin,pfEnd];  
  IF intC.editorType = modeless
    THEN inD.SetCaretBlinking[intC.target.point, intC.cmTextNbr];
  exD.DisplayExceptionString[why];
END;  -- of ReportParseFault --

WarningCarat: PUBLIC PROCEDURE[pfBegin:vmD.CharIndex,pfEnd:vmD.CharIndex] =
BEGIN
  Editor.CancelTargetSelection[];
  Editor.CancelSourceSelection[intC.cmTextNbr];
  inD.StopBlinkingCaret[];
  intC.target ← inD.TextSelection
    [intC.cmTextNbr, pfBegin, pfEnd, pfBegin, 0, char,
    intC.editorType = modeless AND pfBegin < pfEnd];
  intC.pendingDeleteSetByControl ← FALSE;
  Editor.DoTargetSelection[];
  [] ← Editor.MakeCharIndexVisible[pfBegin, intC.cmTextNbr];
END; -- of WarningCarat --

StartTalker[];
InstallTalker[];
WHILE running DO Process.Yield[]; ENDLOOP;

END.  -- of BruceTalker --