-- JaMTajoImpl.mesa
-- Last changed by Doug Wyatt, 23-Oct-81 10:42:23

DIRECTORY
  JaMBasic,
  JaMInternal,
  JaMOps,
  JaMTajo,
  JaMVM,
  JaMFnsDefs USING [PushInteger],
  Ascii USING [CR],
  Directory USING [Error, Lookup],
  File USING [Capability],
  Heap USING [systemZone],
  Process USING [Abort, Detach, EnableAborts],
  Runtime USING [CallDebugger, ConfigError, IsBound, LoadConfig],
  Storage USING [String, FreeString],
  StreamDefs,
  PilotLoaderOps USING [VersionMismatch],
  Tool,
  ToolWindow,
  TajoCedarSwap USING [WhereAmI],
  FormSW,
  TTYSW,
  Put,
  UserInput,
  Window;

JaMTajoImpl: MONITOR
IMPORTS
  JaMOps, JaMVM, JaMFnsDefs,
  Directory, Heap, Process, Runtime, Storage,
  PilotLoaderOps, TajoCedarSwap,
  Tool, ToolWindow, FormSW, TTYSW, Put, UserInput
EXPORTS
  JaMTajo
= {
OPEN TajoPut:Put, StreamDefs, JaMOps, JaMInternal, JaMBasic;

Button: TYPE = JaMTajo.Button;

zone: UNCOUNTED ZONE = Heap.systemZone;

version: STRING ← "JaM of 22-Oct-81";

prompt,start,badfile,badname,badversion: name Object;
buttonName: ARRAY Button OF name Object;

myframe: Frame ← NIL;

State: TYPE = {idle, busy};
state: State ← idle;
readyForInput: CONDITION;
somethingToDo: CONDITION;

objectToDo: Object; -- the object to be executed

tool: Window.Handle ← NIL;
formSW,ttySW: Window.Handle ← NIL;

quit: BOOLEAN ← FALSE;

Quit: PROC[frame: Frame] = {
  quit ← TRUE; Stop[frame];
  };

CallDebugger: PROC[frame: Frame] = {
  Runtime.CallDebugger["JaM executed .calldebugger"L];
  };

LoadBCD: PROC[frame: Frame] = { BCDLoader[frame,FALSE] };
-- Expects opstk: (bcdFileName)
-- Loads and STARTS the configuration in bcdFileName

DebugBCD: PROC[frame: Frame] = { BCDLoader[frame,TRUE] };
-- Expects opstk: (bcdFileName)
-- Like LoadBCD, but invokes the debugger before STARTing

BCDLoader: PROC[frame: Frame, debug: BOOLEAN] = {
  Append: PROC[s,t: STRING] = {
    FOR i: CARDINAL IN[0..t.length) WHILE s.length<s.maxlength DO
      s[s.length] ← t[i]; s.length ← s.length + 1 ENDLOOP };
  name: STRING ← [80];
  ext: STRING ← ".bcd"L;
  extended: BOOLEAN ← FALSE;
  file: File.Capability;
  prog: PROGRAM ← NIL;
  string: string Object ← PopString[frame.opstk];
  IF (string.length+ext.length)>name.maxlength THEN GOTO BadName
  ELSE StringText[string,name];
  file ← Directory.Lookup[name !
    Directory.Error => SELECT type FROM
      fileNotFound => IF extended THEN GOTO BadName
        ELSE { Append[name,ext]; extended ← TRUE; RETRY };
      ENDCASE => GOTO BadName];
  prog ← Runtime.LoadConfig[file: file, offset: 1 !
    Runtime.ConfigError => SELECT type FROM
      versionMismatch => GOTO BadVersion;
      ENDCASE => GOTO BadFile;
    -- The following awful crock brought to you by the purveyors of CedarLoaderCore...
    PilotLoaderOps.VersionMismatch => GOTO BadVersion];
  IF debug THEN {
    message: STRING ← [100];
    Append[message,"JaM: Just loaded "];
    Append[message,name];
    Runtime.CallDebugger[message];
    };
  IF Runtime.IsBound[prog] THEN START prog;
  EXITS
    BadFile => ERROR Error[badfile];
    BadName => ERROR Error[badname];
    BadVersion => ERROR Error[badversion];
  };

JPrint: PROC[frame: Frame] = {
  string: string Object ← PopString[frame.opstk];
  Proc: PROC[c: CHARACTER] RETURNS[BOOLEAN] = {
    abort: BOOLEAN ← GetAbort[frame];
    IF NOT abort THEN TajoPut.Char[ttySW,c];
    RETURN[abort] };
  IF tool=NIL THEN RETURN; -- running on cedar side
  StringForAll[string,Proc];
  };

InstallJaMPilot: PROC[why: InstallReason, frame: Frame] = { SELECT why FROM
  register => {
    found: BOOLEAN;
    badfile ← MakeName[".badfile"L];
    badname ← MakeName[".badname"L];
    badversion ← MakeName[".badversion"L];
    prompt ← MakeName[".prompt"L];
    start ← MakeName[".start"L];
    buttonName[rd] ← MakeName[".reddown"L];
    buttonName[ru] ← MakeName[".redup"L];
    buttonName[yd] ← MakeName[".yellowdown"L];
    buttonName[yu] ← MakeName[".yellowup"L];
    buttonName[bd] ← MakeName[".bluedown"L];
    buttonName[bu] ← MakeName[".blueup"L];

    RegisterExplicit[frame,".print"L,JPrint];
    RegisterExplicit[frame,".loadbcd"L,LoadBCD];
    RegisterExplicit[frame,".debugbcd"L,DebugBCD];
    RegisterExplicit[frame,".calldebugger"L,CallDebugger];
    RegisterExplicit[frame,".quit"L,Quit];

    Def[frame,prompt,MakeString["(*).print"L,X]];
    Def[frame,MakeName[".version"L],MakeString[version]];
    Def[frame, undefname, MakeString["(Undefined name - .undefname: ).print
(                         ).cvis .print"L,X]];
    [found,] ← TryToLoad[frame,start];
    IF NOT found THEN Def[frame,start,MakeString[".version .print (
).print"L,X]];

    };
  ENDCASE;
  };

--DoJaM: PROC = {
  -- Grab the rest of the command line now, before it goes away
--  line ← Storage.String[MAX[200,commandLine.s.length-commandLine.i]];
--  WHILE commandLine.i<commandLine.s.length DO
--    line[line.length] ← commandLine.s[commandLine.i];
--    line.length ← line.length + 1; commandLine.i ← commandLine.i + 1;
--    ENDLOOP;
--  Process.Detach[FORK RunJaM[]];
--  };

RunJaM: PROC = {
  WaitForSomethingToDo: ENTRY PROC = {
    WHILE state=idle DO WAIT somethingToDo ENDLOOP;
    };
  NotifyReadyForMore: ENTRY PROC = {
    state ← idle; NOTIFY readyForInput;
    };
  lineLoop: PROCESS ← NIL;
  StartJaM["JaM.VM"L];
  myframe ← defaultFrame;
  quit ← FALSE;

  -- do start macro
  Execute[myframe,start];

  -- do command line
 -- s.reset[s]; stream ← MakeStream[s];
 -- stream.tag ← X; Execute[myframe,stream];

  Process.Detach[lineLoop ← FORK LineLoop[]];
  state ← idle;
  UNTIL quit DO
    WaitForSomethingToDo[!ABORTED => { quit ← TRUE; EXIT }];
    Execute[myframe,objectToDo];
    NotifyReadyForMore[];
    ENDLOOP;

  Process.Abort[lineLoop];
  myframe ← NIL;
  StopJaM[];
  IF tool# NIL THEN ToolWindow.Destroy[tool];
  };

Do: ENTRY PROC[ob: Object] = {
  UNTIL state=idle DO WAIT readyForInput ENDLOOP;
  objectToDo ← ob; state ← busy; NOTIFY somethingToDo;
  UNTIL state=idle DO WAIT readyForInput ENDLOOP;
  };
    
LineLoop: PROC = {
  line: STRING ← Storage.String[200];
  index: CARDINAL ← 0;
  Reset: PROC[s: StreamHandle] = { index ← 0 };
  Get: PROC[s: StreamHandle] RETURNS[UNSPECIFIED] = {
    i: CARDINAL ← index;
    IF i<line.length THEN { index ← i + 1; RETURN[line[i]] }
    ELSE RETURN[0C] };
  Putback: PROC[s: StreamHandle, x: UNSPECIFIED] = {
    IF index>0 THEN index ← index - 1 };
  Endof: PROC[s: StreamHandle] RETURNS[BOOLEAN] = {
    RETURN[index>=line.length] };
  Put: PROC[s: StreamHandle, x: UNSPECIFIED] = { };
  Destroy: PROC[s: StreamHandle] = { };
  sobject: StreamObject ← [reset: Reset, get: Get, putback: Putback,
    endof: Endof, put: Put, destroy: Destroy, data: NIL];
  stream: StreamHandle ← @sobject;
  EndLine: PROC[c: CHARACTER] RETURNS[BOOLEAN] = {
    IF c=Ascii.CR THEN RETURN[LineComplete[line]] ELSE RETURN[FALSE] };
  IF ttySW=NIL THEN RETURN;
  UNTIL quit DO
    Do[prompt];
    [] ← TTYSW.GetEditedString[ttySW,line,EndLine,TRUE !
      TTYSW.LineOverflow => IF s=line THEN {
	new: STRING ← Storage.String[s.maxlength+s.maxlength/2];
	FOR i: CARDINAL IN[0..s.length) DO new[i] ← s[i] ENDLOOP;
	new.length ← s.length; line ← new; Storage.FreeString[s];
	RESUME[new] };
      TTYSW.Rubout => { TajoPut.Line[ttySW," XXX"L]; line.length ← 0; CONTINUE };
      ANY => EXIT];
    IF quit THEN EXIT;
    TajoPut.CR[ttySW]; index ← 0;
    Do[MakeStream[stream,X]];
    ENDLOOP;
  Storage.FreeString[line];
  };

MouseProc: TYPE = JaMTajo.MouseProc;

mouseProc: MouseProc ← DefaultMouseProc;

DefaultMouseProc: PROC[x,y: INTEGER] = {
  JaMFnsDefs.PushInteger[x];
  JaMFnsDefs.PushInteger[y];
  };

SetMouseProc: PUBLIC PROC[new: MouseProc] RETURNS[MouseProc] = {
  old: MouseProc ← mouseProc;
  mouseProc ← new; RETURN[old];
  };

DoButton: PUBLIC ENTRY PROC[button: Button, x,y: INTEGER] = {
  IF state#idle THEN RETURN; -- ignore buttons if not idle
  mouseProc[x,y]; -- push transformed coordinates
  objectToDo ← buttonName[button];
  state ← busy; NOTIFY somethingToDo;
  };

-- Tajo stuff

InitTool: PROC = {
  IF TajoCedarSwap.WhereAmI[] = cedar THEN RETURN;
  tool ← Tool.Create[
    makeSWsProc: MakeSWs, initialState: active,
    clientTransition: ClientTransition, name: version];
  ToolWindow.SetTinyName[tool,"JaM"L];
  };

AcceptTypeInFrom: PUBLIC PROC[w: Window.Handle] = {
  IF ttySW=NIL THEN RETURN;
  UserInput.CreateIndirectStringInOut[from: w, to: ttySW];
  UserInput.SetKeyPNR[w, keyboard, UserInput.TypeInPNR];
  UserInput.SetCursorPNR[w, UserInput.TypeInCursorPNR];
  };

ClientTransition: ToolWindow.TransitionProcType = {
  IF old=active AND new#active THEN JaMVM.Check[];
  };

logName: STRING ← [20];

MakeSWs: Tool.MakeSWsProc = {
  Tool.UnusedLogName[unused: logName, root: "JaM.log"L];
  formSW ← Tool.MakeFormSW[window: window, formProc: MakeForm];
  ttySW ← Tool.MakeTTYSW[window: window, name: logName];
  };

MakeForm: FormSW.ClientItemsProcType = { OPEN FormSW;
  nItems: CARDINAL = 2;
  items ← AllocateItemDescriptor[nItems];
  items[0] ← CommandItem[tag: "Interrupt"L,
    place: newLine, proc: InterruptButton];
  items[1] ← StringItem[tag: "Logged on"L,
    place: nextPlace, string: @logName, readOnly: TRUE];
  RETURN[items: items, freeDesc: TRUE];
  };

InterruptButton: FormSW.ProcType = {
  IF myframe#NIL THEN SetAbort[myframe,TRUE];
  };

-- Initialization

Install[InstallJaMPilot];
Process.EnableAborts[@somethingToDo];
InitTool[];
Process.Detach[FORK RunJaM[]];

}.