/* The Network class is a formal class which defines basic
   connection methods for nodes. The network maintains the
   start and end nodes as well as two dictionaries of nodes.
   NodeNames uses the name as key, display uses the relative
   position as key for easy window "hit testing" etc. */!!

inherit(Object, #Network, #(name       /* network name */
desc       /* longer description */
start      /* starting node */
end        /* ending node */
nodeNames  /* dictionary by name */
display    /* dictionary by posn */), 2, nil)!!

now(NetworkClass)!!

/* Create a new network. */
Def  new(self)
{ 
  ^init(new(self:Behavior));
}  !!    

now(Network)!!

/* Return the node if the name exists in the
   nodeNames dictionary, otherwise return false and
   display an error message. */
Def  checkNodeExists(self, nodeName | node)
{
  if not(node := nodeExists(self, nodeName))
    beep();
    errorBox(loadString(PW_NODEINVAL), 
      nodeName + loadString(PW_NOTEXIST));
  endif;
  ^node;
}!!

/* Adjust the display as necessary.
   Called by setPosn, resetPosn in class Node. */
Def  resetPosn(self, aNode, oldPos)
{    
  if (oldPos in display) = aNode
    remove(display, oldPos);
  endif;
  
  /* if there is a conflict adjust as necessary */
  /* the resulting diagram may not be perfect */
  
  if pos(aNode) in display      /* conflict */
    if aNode.y > 0              /* not top row */
      aNode.y := aNode.y - 1;   /* go up */
    endif;
    loop 
    while pos(aNode) in display
      aNode.y := aNode.y + 1;   /* go down */
    endLoop;
  endif;
   
  add(display, pos(aNode), aNode);
}!!

/* Return the number of nodes in the network. */
Def  size(self)
{
  ^size(nodeNames);
}!!

/* Given a point position, lookup the node. */
Def  displayLookup(self, pos)
{ 
  ^display[pos];
}!!

/* Return a dictionary of nodes. */
Def  nodes(self)
{
  ^nodeNames;
}!!

/* Return the type of nodes that start and end should be.
   Descendants will redefine this method. */
Def  nodeClass(self)
{
  ^Node;
}!!

/* Remove a node from the node table. */
Def  removeNode(self, aNode)
{ 
  remove(nodeNames, getName(aNode));
}!!

/* Add a node to the node table.  Use it's name as the key. */
Def  addNode(self, aNode)
{ 
  add(nodeNames, getName(aNode), aNode);
}!!

/* Check if the nodes name1 and name2 exist.  If so,
   disconnect node1->node2.  */
Def  disconnect(self, name1, name2 | node1, node2)
{
  node1 := checkNodeExists(self, name1);
  node2 := checkNodeExists(self, name2);
  if node1 cand node2
    disconnect(node1, node2);
  endif;
}!!

/* Return the node if the name exists in the
   nodeNames dictionary, otherwise return false.
   If error checking is required, use checkNodeExists. */
Def  nodeExists(self, nodeName | node)
{
  ^nodeName in nodeNames;
}!!

/* Check if the nodes name1 and name2 exist.  If so,
   connect node1->node2.  */
Def  connect(self, name1, name2 | node1, node2)
{
  node1 := checkNodeExists(self, name1);
  node2 := checkNodeExists(self, name2);
  if node1 cand node2
    connect(node1, node2);
  endif;
}!!

/* Get the description. */
Def  getDesc(self)
{ ^desc;
} !!

/* Show a diagram of the network.  Keep track
   of visited nodes to avoid looping. 
   Note: Only for use during development. */
Def  show(self | visited)
{ 
  printLine("");
  printLine(self);            /* title */
  CurPos:=0;                  /* global variable */
  visited := new(Set,10);
  add(visited,start); 
  show(start, visited, 0);    /* begin recursing */
  printLine("");
}!!

/* Set the name. */
Def  setName(self, aName)
{ name := aName;
}  !! 

/* Get the name. */
Def  getName(self)
{ ^name;
} !!

/* Initialize a new Network. Create the start and end nodes
   and add them to the dictionaries.  The dictionaries will
   grow in size as necessary.  Uses the nodeClass method
   to determine what type of nodes start and end should be.  */
Def  init(self)
{ name := "";
  desc := "";
  start := new(nodeClass(self));
  setName(start,"Start");
  end := new(nodeClass(self));
  setName(end,"End");
  end.x := 0; end.y := 1;    /* default location */
  start.network := end.network := self;
  
  nodeNames := new(Dictionary, 10);     /* by name */
  nodeNames["Start"] := start;
  nodeNames["End"] := end;
  
  display := new(Dictionary, 10);       /* by posn */
  add(display, pos(start), start);
  add(display, pos(end), end);
} !! 

/* print the network name */
Def  printOn(self, aStream)
{ printOn(asString(class(self)) + "(" + name + ")",
  aStream);
} !!

