(* Copyright 1993 Digital Equipment Corporation.                             *)
(* Distributed only by permission.                                           *)
(*                                                                           *)
(* Last modified on Mon Aug  2 20:06:00 PDT 1993 by rsanchez                 *)
(*      modified on Fri Jul 16 21:27:42 PDT 1993 by heydon                   *)

(* View for packet routing in a graph *)

(

let nodeColor = [color_named("yellow"), color_named("red")];
let edgeColor = [color_named("Blue"), color_named("Orange")];
let packetColor = [color_named("VeryLightGreen"), color_named("VeryDarkGreen")];
let labelColor = color_named("Black");
let packetDiscardColor = color_named("DarkRed");
let borderColor = color_named("Black");
let backgroundColor = color_named("White");

let normal = 0;
let congested = 1;

let one = 0;
let two = 0;
let twoplus = 1;

let edgeWidth = 0.1;
let borderWidth = 0.1;

let phase1 = 1;
let phase2 = 2;

let StraightEdge =
  {
    g => ok,
    e => ok,
    v1 => ok,
    v2 => ok,
    nodeSize => ok,
    midpt12 => ok,
    midpt21 => ok,

    edge => meth(self) self.e end,

    create => meth (self, t, v1, v2)
      let inBetweenPoint = proc(pt1, pt2, fract)
	 var pt = [0.0, 0.0];
	 if pt1[0] << pt2[0] then
	   pt[0] := pt1[0] ++ (fract ** (pt2[0] -- pt1[0]));
         else
	   pt[0] := pt1[0] -- (fract ** (pt1[0] -- pt2[0]));
         end;
	 if pt1[1] << pt2[1] then
	   pt[1] := pt1[1] ++ (fract ** (pt2[1] -- pt1[1]));
         else
	   pt[1] := pt1[1] -- (fract ** (pt1[1] -- pt2[1]));
         end;
         pt
      end;

      self.g := t.g;
      self.nodeSize := t.nodeSize;
      self.v1 := v1;
      self.v2 := v2;
      self.midpt12 := inBetweenPoint(self.v1.pos, self.v2.pos, 1.0//3.0);
      self.midpt21 := inBetweenPoint(self.v2.pos, self.v1.pos, 1.0//3.0);

      self.e := graph_newEdge(v1.v, v2.v);
      graph_moveEdge(self.e, v1.v, v2.v, false);
      graph_setEdgeColor(self.e, edgeColor[normal]);
      self
    end,

    initialWidth => meth(self, size)
      graph_setEdgeWidth(self.edge, size//10.0);
    end,

    movePktAlongEdge => meth(self, nodeid, vpkt, phase)
      if phase is phase1 then
	if nodeid is self.v1.id then 
	  graph_moveVertex(vpkt, self.midpt12[0], self.midpt12[1], true);
        else
	  graph_moveVertex(vpkt, self.midpt21[0], self.midpt21[1], true);
        end;
      else
	if nodeid is self.v1.id then
	  graph_moveVertex(vpkt, self.v1.pos[0], self.v1.pos[1], true);
        else
	  graph_moveVertex(vpkt, self.v2.pos[0], self.v2.pos[1], true);
        end;
      end;
    end,

};


let CurvedEdge =
  {
    g => ok,
    e => ok,
    v1 => ok,
    v2 => ok,
    ctls => ok,

    edge => meth(self) self.e end,

    create => meth (self, t, v1, v2, x1, y1, x2, y2)
      let invisible = proc(pt)
        let res = graph_newVertex(t.g);
        graph_setVertexSize(res, 0.0, 0.0);
        graph_moveVertex(res, pt[0], pt[1], false);
        res
      end;
      self.g := t.g;
      self.v1 := v1;
      self.v2 := v2;
      let cpt1 = [x1, y1];
      let cpt2 = [x2, y2];
      var cpt1v = invisible(cpt1);
      var cpt2v = invisible(cpt2);
      self.ctls := [cpt1v, cpt2v]; 
      self.e := graph_newEdge(v1.vertex, v2.vertex);
      graph_moveEdgeBezier(self.e, v1.vertex, v2.vertex, cpt1v, cpt2v, false);
      graph_setEdgeColor(self.e, edgeColor[normal]);
      graph_setEdgeWidth(self.e, edgeWidth);
      self
    end,

    initialWidth => meth(self, size)
      graph_setEdgeWidth(self.edge, size//10.0);
    end,

    movePktAlongEdge => meth(self, srcid, vpkt, phase)
      ok
    end,
};


let Node =
  {
    g => ok,
    v => ok,
    qs => 0,
    id => ok,
    maxQS => ok,
    pos => ok,
    vpkts => ok,
    pktsSize => ok,
    pktsPos => ok,

    pktOutLst => [],
    pktInLst => [],

    vertex => meth(self) self.v end,

    incQS => meth(self)
      self.qs := self.qs + 1;
    end,

    decQS => meth(self)
      self.qs := self.qs - 1;
    end,

    updQS => meth(self, qs)
       self.qs := qs;
    end,

    getQS => meth(self) self.qs end,

    create => meth(self, t, id, x, y)
      self.g := t.g;
      self.id := id;
      self.maxQS := t.maxQS;
      self.pos := [x, y];
      self.v := graph_newVertex(self.g);
      graph_moveVertex(self.v, x, y, false); 
      graph_setVertexShape(self.v, "ellipse");
      graph_setVertexColor(self.v, nodeColor[normal]); 
      self
    end,

    initialSize => meth(self, size)
      graph_setVertexSize(self.v, size, size);
      graph_setVertexBorder(self.v, size//10.0);
      graph_setVertexBorderColor(self.v, borderColor);
      self.vpkts := graph_newVertex(self.g);
      self.pktsSize := size//3.0;
      self.pktsPos := [self.pos[0] -- (size//9.0),
		       self.pos[1] ++ (size//9.0)];
      graph_moveVertex( self.vpkts, self.pktsPos[0], self.pktsPos[1], false);
      graph_setVertexSize(self.vpkts, 0.0, 0.0);
      graph_setVertexBorder(self.vpkts, self.pktsSize//5.0);
      graph_setVertexBorderColor(self.vpkts, borderColor);
    end,

    updateNodeState => meth(self)
      let qs = self.qs;
      if (qs is 0) or (qs is 1) then
        graph_setVertexColor(self.v, nodeColor[normal]);
	graph_setVertexSize(self.vpkts, 0.0, 0.0);
      else
        if qs >= self.maxQS then
          graph_setVertexColor(self.v, nodeColor[congested]);
        else
          graph_setVertexColor(self.v, nodeColor[normal]);
        end;
        graph_setVertexSize(self.vpkts, self.pktsSize, self.pktsSize);
	if self.qs is 2 then
	  graph_setVertexColor(self.vpkts, packetColor[two]);
        else
	  graph_setVertexColor(self.vpkts, packetColor[twoplus]);
        end;
      end;
    end,

    pktOut => meth(self, pkt, edge)
      self.pktOutLst := self.pktOutLst @
          [{pkt => pkt, edge => edge}];
    end,

    pktIn => meth(self, pkt, edge, discard)
      self.pktInLst := self.pktInLst @
          [{pkt => pkt, edge => edge, discard => discard}];
    end,

};

let Packet =
  {
    g => ok,
    v => ok,
    src => ok,
    dst => ok,
    pktPos => ok,

    vertex => meth(self) self.v end,

    create => meth(self, t, src, dst, name)
      self.g := t.g;
      self.src := src;
      self.dst := dst;

      self.v := graph_newVertex(self.g);
      self.pktPos := [t.node(src).pos[0],
		      t.node(src).pos[1]];
      graph_moveVertex( self.v, self.pktPos[0], self.pktPos[1], false);
      graph_setVertexSize(self.v, t.nodeSize//3.0, t.nodeSize//3.0);
      graph_setVertexBorder(self.v, (t.nodeSize//3.0)//5.0);
      graph_setVertexBorderColor(self.v, borderColor);
      graph_setVertexColor(self.v,  packetColor[one]);

      t.node(src).incQS();
      self
    end,

    move => meth(self, t, fromN, toN)
      graph_moveVertex( 
	  self.v, 
	  t.node(fromN).pos[0], 
	  t.node(fromN).pos[1], true);
      t.node(fromN).decQS;
      let edge = t.edge(fromN, toN);
      t.node(fromN).pktOut(self, edge);
      if toN is self.dst then
        t.node(toN).pktIn(self, edge, true);
      else
        t.node(toN).pktIn(self, edge, false);
      end;
      graph_vertexToFront(self.v);
    end,

    setDiscardColor => meth(self)
      graph_setVertexColor(self.v, packetDiscardColor);
      graph_vertexToFront(self.v);
    end,

    discard => meth(self)
      graph_setVertexSize(self.v, 0.0, 0.0);
    end,

    updatePos => meth(self, t, vnode)
      self.pktPos := [vnode.vtx.pos[0] ++ (t.nodeSize//15.0),
		      vnode.vtx.pos[1] -- (t.nodeSize//15.0)];
      graph_moveVertex(self.v, self.pktPos[0], self.pktPos[1], true);
    end,

};

let view =
  {
    graphvbt => graph_new(),
    g => meth (self) self.graphvbt end,

    nodeCnt => ok,
    maxQS => ok,
    bounded => ok,

    nodeSize => 0.0,

    vertexFont => ok,

    nodeT => [],
    edgeT => [],
    pktT => [],

    node => meth(self, id)
      var res = ok;
      for i = 0 to #(self.nodeT)-1 do
        if self.nodeT[i].nodeID is id then
          res := self.nodeT[i].vtx;
          exit;
        end;
      end;
      res
    end,

    edge => meth(self, id1, id2)
      var res = ok;
      for i = 0 to #(self.edgeT)-1 do
        if ((self.edgeT[i].nodeID1 is id1) and 
            (self.edgeT[i].nodeID2 is id2)) orif 
           ((self.edgeT[i].nodeID1 is id2) and
            (self.edgeT[i].nodeID2 is id1)) then
          res := self.edgeT[i].edge;
          exit;
        end;
      end;
      res
    end,

    packet => meth(self, id)
      var res = ok;
      for i = 0 to #(self.pktT)-1 do
	if self.pktT[i].pktID is id then
	  res := self.pktT[i].vtx;
	  exit;
        end;
      end;
      res
    end,

   StartGraph => meth (self, nodeCnt, maxQS, bounded, xMax, yMax)
      (* Set the World *)
      let n = yMax,
	  s = 0.0,
	  w = 0.0,
	  e = xMax;
      graph_setWorld(self.graphvbt, w, e, n, s);
      self.vertexFont := graph_newFont(
	                self.graphvbt, "Helvetica", 0.1, "Roman", "bold", "*");

      (* Initialize some variables *)
      self.nodeCnt := nodeCnt;
      self.maxQS := maxQS;
      self.bounded := bounded;
   end,

    NewNode => meth (self, id, x, y)
      self.nodeT :=
	self.nodeT @
          [{nodeID => id,
            vtx => clone(Node).create(self, id, x, y)}];
    end,

    NewStraightEdge => meth (self, id1, id2)
      self.edgeT :=
        self.edgeT @
          [{nodeID1 => id1,
            nodeID2 => id2,
            edge => clone(StraightEdge).create(
                         self, self.node(id1), self.node(id2))}];
    end,

    NewCurvedEdge => meth (self, id1, id2, x1, y1, x2, y2)
      self.edgeT :=
        self.edgeT @
          [{nodeID1 => id1,
            nodeID2 => id2,
            edge => clone(CurvedEdge).create(
                          self, self.node(id1), self.node(id2), 
                          x1, y1, x2, y2)}];
    end,

    NewLabel => meth (self, label, x, y)
      let vl = graph_newVertex(self.graphvbt);
      graph_setVertexFont(vl, self.vertexFont);
      graph_setVertexSize(vl, 0.2, 0.2);
      graph_setVertexColor(vl, backgroundColor);
      graph_moveVertex(vl, x, y, false);
      graph_setVertexLabel(vl, label);
      graph_setVertexLabelColor(vl, labelColor);
    end,

    EndGraph => meth (self)
      let distance = proc(pt1, pt2)
        let dst = math_sqrt(((pt2[1]--pt1[1])**(pt2[1]--pt1[1])) ++
                            ((pt2[0]--pt1[0])**(pt2[0]--pt1[0])));
        dst
      end;
      var minDst = 0.0;
      for i = 0 to #(self.nodeT) - 1 do
	let pos1 = self.nodeT[i].vtx.pos;
	for j = 0 to #(self.nodeT) - 1 do
	  if i isnot j then
	    let pos2 = self.nodeT[j].vtx.pos;
	    let curDst = distance(pos1,pos2);
            if (minDst is 0.0) orif (minDst >> curDst) then
              minDst := curDst;
            end;
          end;
        end;
      end;
      self.nodeSize := minDst//1.5;
      for i = 0 to #(self.nodeT) - 1 do
        self.nodeT[i].vtx.initialSize(self.nodeSize);
      end;
      for i = 0 to #(self.edgeT) - 1 do
        self.edgeT[i].edge.initialWidth(self.nodeSize);
      end;
    end,

    StartPackets => meth (self, pktCnt)
      ok;
    end,

    NewPacket => meth (self, id, source, dest, fewestHops, name)
      self.pktT :=
	self.pktT @
          [{pktID => id,
            vtx => clone(Packet).create(self, source, dest, name)}];
    end,

    EndPackets => meth (self)
      for i = 0 to #(self.nodeT) - 1 do
        self.nodeT[i].vtx.updateNodeState();
      end;
      graph_redisplay(self.graphvbt);
    end,

    MovePacket => meth (self, id, fromN, toN)
      self.packet(id).move(self, fromN, toN);
    end,

    Step => meth (self, queueSizes)
      (* Phase 1 *)
      for i = 0 to #(self.nodeT) - 1 do
        if #(self.nodeT[i].vtx.pktOutLst) isnot 0 then
          for j = 0 to #(self.nodeT[i].vtx.pktOutLst) - 1 do
            self.nodeT[i].vtx.pktOutLst[j].edge.movePktAlongEdge
                 (i, self.nodeT[i].vtx.pktOutLst[j].pkt.v, phase1);
          end;
        end;
      end;
      for i = 0 to #(self.nodeT) - 1 do
        if #(self.nodeT[i].vtx.pktOutLst) isnot 0 then
          self.nodeT[i].vtx.updateNodeState();
          self.nodeT[i].vtx.pktOutLst := [];
        end;
      end;
      zeus_animate(self.graphvbt, 0.0, 0.35);
      (* Phase 2 *)
      for i = 0 to #(self.nodeT) - 1 do
        if #(self.nodeT[i].vtx.pktInLst) isnot 0 then
          for j = 0 to #(self.nodeT[i].vtx.pktInLst) - 1 do
            self.nodeT[i].vtx.pktInLst[j].edge.movePktAlongEdge
                 (i, self.nodeT[i].vtx.pktInLst[j].pkt.v, phase2);
          end;
        end;
      end;
      zeus_animate(self.graphvbt, 0.35, 1.0);
      for i = 0 to #(self.nodeT) - 1 do
        self.nodeT[i].vtx.updQS(queueSizes[i]);
        self.nodeT[i].vtx.updateNodeState();
        if #(self.nodeT[i].vtx.pktInLst) isnot 0 then
          for j = 0 to #(self.nodeT[i].vtx.pktInLst) - 1 do
            if self.nodeT[i].vtx.pktInLst[j].discard is true then
              self.nodeT[i].vtx.pktInLst[j].pkt.setDiscardColor();
            end;
	    (*
              self.nodeT[i].vtx.pktInLst[j].pkt.updatePos(self, self.nodeT[i]);
	      *)
          end;
        end;
      end;
      graph_redisplay(self.graphvbt);
      for i = 0 to #(self.nodeT) - 1 do
	if #(self.nodeT[i].vtx.pktInLst) isnot 0 then
	  for j = 0 to #(self.nodeT[i].vtx.pktInLst) - 1 do
	    if self.nodeT[i].vtx.pktInLst[j].discard is true then
	      self.nodeT[i].vtx.pktInLst[j].pkt.discard();
            end;
          end;
          self.nodeT[i].vtx.pktInLst := [];
        end;
      end;
      graph_redisplay(self.graphvbt);
    end,

  };

view;

);

