-- File: GrapenutImpl.mesa
-- Contents: whitepages facilities, running on top of Cypress, Squirrel, and Finch
-- Created by Cattell, January 1984
-- Last edited by:
-- Cattell on January 13, 1984 5:36 pm

DIRECTORY
CommandTool,
Commander,
  DB,
FinchSmarts USING [PlaceCall],
IO,
Menus,
Nut,
  NutOps,
  NutViewer,
  Rope,
PrincOpsUtils USING [IsBound],
  ViewerClasses USING [Viewer],
ViewerOps USING [PaintViewer],
  UserCredentials;

GrapenutImpl: CEDAR PROGRAM
IMPORTS
CommandTool,
Commander,
  DB,
FinchSmarts,
IO,
Menus,
Nut,
  NutOps,
  NutViewer,
  Rope,
  PrincOpsUtils,
  ViewerOps,
  UserCredentials
 = BEGIN

ROPE: TYPE = Rope.ROPE;

PhoneProc: Commander.CommandProc = TRUSTED {
ENABLE IO.EndOfStream => {msg← "Illegal syntax"; CONTINUE};
number: ROPE; t: DB.Relship; phone: DB.Relation; phoneOf, phoneIs, phoneAt: DB.Attribute;
h: IO.STREAM = IO.RIS[cmd.commandLine];
skip: CHAR = h.GetChar[]; -- skip leading space
name: ROPE = h.GetLineRope[];
person: DB.Entity = StringToPerson[name];
IF person=NIL THEN RETURN[NIL, Rope.Cat["Found no Person or RegisteredName ", name]];
phone← DB.DeclareRelation["phone", DB.SegmentOf[person], OldOnly];
phoneOf← DB.DeclareAttribute[phone, "of"];
phoneIs← DB.DeclareAttribute[phone, "is"];
phoneAt← DB.DeclareAttribute[phone, "at"];
t← DB.DeclareRelship[phone, LIST[[phoneOf, person], [phoneAt, DB.S2V["work"]]], OldOnly];
IF t=NIL THEN RETURN[NIL, Rope.Cat["Found no phone number for ", name]];
number ← DB.V2S[DB.GetF[t, phoneIs]];
IF NOT PrincOpsUtils.IsBound[FinchSmarts.PlaceCall] THEN {
err: ROPE;
NutViewer.MessageRope[NIL,  "Loading and starting Finch... "];
err← CommandTool.Run["Finch"].errMsg;
IF err.Length[]#0 THEN
NutViewer.Message[NIL, err]
ELSE {
[] ← CommandTool.DoCommandRope[
commandLine: "Finch",
parent: NEW[Commander.CommandObject ← []]];
NutViewer.MessageRope[NIL, "Done."] } };
FinchSmarts.PlaceCall[number: number, rName: DB.NameOf[person]];
};

PhoneHomeProc: Menus.MenuProc = {
viewer: ViewerClasses.Viewer = NARROW[parent];
seg: DB.Segment; d: DB.Domain; eName: ROPE;
ok: PROC[s: ROPE] RETURNS[BOOL] = {RETURN[s.Equal["home", FALSE]]};
[seg, d, eName]← NutViewer.GetNutInfo[viewer];
Phone[DB.FetchEntity[d, eName, seg], ok];
};

PhoneWorkProc
: Menus.MenuProc = {
viewer: ViewerClasses.Viewer = NARROW[parent];
seg: DB.Segment; d: DB.Domain; eName: ROPE;
ok: PROC[s: ROPE] RETURNS[BOOL] = {RETURN[NOT s.Equal["home", FALSE]]};
[seg, d, eName]← NutViewer.GetNutInfo[viewer];
Phone[DB.FetchEntity[d, eName, seg], ok];
};

Phone: PROC [e: DB.Entity, ok: PROC[ROPE] RETURNS [BOOL]] = {
seg: DB.Segment = DB.SegmentOf[e];
phone: DB.Relation = DB.DeclareRelation["phone", seg, OldOnly];
phoneOf: DB.Attribute = DB.DeclareAttribute[r: phone, name: "of", version: OldOnly];
phoneIs: DB.Attribute = DB.DeclareAttribute[r: phone, name: "is", version: OldOnly];
phoneAt: DB.Attribute = DB.DeclareAttribute[r: phone, name: "at", version: OldOnly];
phones: DB.RelshipSet← DB.RelationSubset[phone, LIST[[phoneOf, e]]];
FOR pRel: DB.Relship← DB.NextRelship[phones], DB.NextRelship[phones] UNTIL pRel=NIL DO
IF ok[DB.GetFS[pRel, phoneAt]] THEN TRUSTED {FinchSmarts.PlaceCall[
number: DB.GetFS[pRel, phoneIs], rName: DB.NameOf[e], useNumber: TRUE]; RETURN};
ENDLOOP;
NutViewer.Error[NIL, "Can't find phone number for ", DB.NameOf[e]];
};

PersonProc: Commander.CommandProc = {
ENABLE IO.EndOfStream => {msg← "Illegal syntax"; CONTINUE};
h: IO.STREAM = IO.RIS[cmd.commandLine];
skip: CHAR = h.GetChar[]; -- skip leading space
name: ROPE = h.GetLineRope[];
person: DB.Entity = StringToPerson[name];
IF person#NIL THEN []← Nut.Display[person]
ELSE msg← Rope.Cat["Found no Person or RegisteredName ", name];
};

StringToPerson: PROC[name: ROPE] RETURNS [person: DB.Entity] = {
-- Try to find the named person. Look in Squirrel, GrapenutLocal, and GrapenutRemote
-- segments. Try name as an RName, then as a prefix of a real name.
  localFile: ROPE← Rope.Cat["[Luther.Alpine]<", UserCredentials.Get[].name, ">Squirrel.segment"];
IF name=NIL THEN RETURN[NIL];
IF NutOps.SetUpSegment[localFile, $Squirrel].success THEN {
person← TryAsRName[name, $Squirrel]; IF person#NIL THEN RETURN;
person← TryAsName[name, $Squirrel]; IF person#NIL THEN RETURN};
IF NutOps.SetUpSegment[
"[Luther.Alpine]<Grapenut>Grapenut.segment", $Grapenut].success THEN {
person← TryAsRName[name, $Grapenut]; IF person#NIL THEN RETURN;
person← TryAsName[name, $Grapenut]; IF person#NIL THEN RETURN};
};

TryAsRName: PROC[name: ROPE, seg: DB.Segment] RETURNS [person: DB.Entity] = {
RName: DB.Domain = DB.DeclareDomain["RegisteredName", seg, OldOnly];
mailbox: DB.Relation = DB.DeclareRelation["mailbox-name", seg, OldOnly];
mailboxOf: DB.Attribute = DB.DeclareAttribute[mailbox, "of"];
IF RName=NIL THEN RETURN[NIL];
person← DB.FetchEntity[RName, name];
IF person=NIL THEN RETURN[NIL];
person← DB.V2E[DB.GetP[person, mailboxOf]];
};

TryAsName
: PROC[name: ROPE, seg: DB.Segment] RETURNS [person: DB.Entity] = {
es: DB.EntitySet; other: DB.Entity;
count: INT← 0;
People: DB.Domain = DB.DeclareDomain["Person", seg, OldOnly];
IF People=NIL THEN RETURN[NIL];
es← DB.DomainSubset[People, name, name.Concat["\177"]];
person← DB.NextEntity[es];
IF (other← DB.NextEntity[es])#NIL THEN BEGIN
NutViewer.MessageRope[NIL, "Name is not unique! Matches: "];
NutViewer.MessageRope[NIL, DB.NameOf[person]]; NutViewer.MessageRope[NIL, "; "];
NutViewer.MessageRope[NIL, DB.NameOf[other]]; NutViewer.MessageRope[NIL, "; "];
WHILE (other← DB.NextEntity[es])#NIL DO
IF (count← count+1)> 5 THEN {NutViewer.MessageRope[NIL, " ... "]; EXIT};
NutViewer.MessageRope[NIL, DB.NameOf[other]]; NutViewer.MessageRope[NIL, "; "];
ENDLOOP;
person← NIL;
END;
};

MyDisplay: Nut.DisplayProc =
-- For Organizations and Persons
BEGIN
Nut.DefaultDisplay[e, newV, seg];
IF PrincOpsUtils.IsBound[FinchSmarts.PlaceCall] THEN {
-- Ambush the menu and add Phone, PhoneHome items on the end!
Menus.AppendMenuEntry[newV.menu, Menus.CreateEntry[
"Phone", PhoneWorkProc,, "Call this person at work number"]];
Menus.AppendMenuEntry[newV.menu, Menus.CreateEntry[
"PhoneHome", PhoneHomeProc,, "Call this person at home number"]];
ViewerOps.PaintViewer[newV, menu]; -- Repaint the menu
};
END;

Nut.Register["Person", NIL, MyDisplay];
Nut.Register["Organization", NIL, MyDisplay];

Commander.Register[
"Person", PersonProc, "Display person with given RName or name (any prefix, lastname first)"];
Commander.Register[
"Ring", PhoneProc, "Phones person with given RName or real name (prefix, lastname first)"];

END.