package testprocs;

import simulator.*;
import modules.color.*;
import modules.codegas.*;
import modules.line.*;
import modules.neighborset.*;
import modules.node.*;
import modules.gradient.*;
import modules.region.*;
import utils.*;
import java.awt.*;
import java.util.*;

// Persistent Node testing

public class PNTestProc implements ProcessorModule, Observer {
  public Symbol getName() { return Symbol.GetSymbol("MyProc"); }
  public void link(PortedProcessor p) {
    pp = p;
    pp.addClockListener(this);
    pp.openNetworkPort(this,uiPort);
    ag = (ActiveGradient)p.getModule("ActiveGradient");
    cm = (ColorModule)p.getModule("Color");
    pn = (PersistentNodeB)p.getModule("PersistentNode");
    rm = (RegionManager)p.getModule("RegionManager");
  }

  PortedProcessor pp; ActiveGradient ag; ColorModule cm; PersistentNodeB pn;
  RegionManager rm;

  static final Symbol uiPort = Symbol.GetSymbol("UI");
  static final Symbol test = Symbol.GetSymbol("test");
  static final Symbol test2 = Symbol.GetSymbol("test2");

  static double initiator = 0;
  static double initiator2 = 0;
  public void init() {
    if(initiator==0) { 
      initiator = pp.getUID();
      //pn.initiateNode(test);
    } else if(initiator2==0) {
      initiator2 = pp.getUID();
      //pn.initiateNode(test2);
    }
  }


  Vector pendingAck=new Vector();
  int time=0;

  static final Symbol initCmd = Symbol.GetSymbol("init");
  static final Symbol initinCmd = Symbol.GetSymbol("initin");
  static final Symbol killCmd = Symbol.GetSymbol("kill");
  static final Symbol getCmd = Symbol.GetSymbol("get");
  static final Symbol setCmd = Symbol.GetSymbol("set");
  public void update(Observable o, Object arg) {
    pendingAck.add(new Pair(new Integer(time+18),"Write completed for "+pp.getUID()));
    //System.out.println("Write completed for "+pp.getUID());
  }
  public boolean signalEvent(Symbol name,Object data) {
    boolean dirty = false;
    if(name==uiPort) {
      System.out.println(time+": recv msg: "+data);
      StringTokenizer tok = new StringTokenizer(data.toString());
      if(tok.hasMoreTokens()) { 
        Symbol cmd=Symbol.GetSymbol(tok.nextToken());
        if(cmd==initCmd || cmd==initinCmd) {
          Symbol region = RegionManager.rootID;
          if(cmd==initinCmd && tok.hasMoreTokens())
            region = Symbol.GetSymbol(tok.nextToken());
          Object nodename = new Double(pp.getUID());
          int size = 3; double umbra=3.0;
          if(tok.hasMoreTokens()) {
            nodename=Symbol.GetSymbol(tok.nextToken());
            if(tok.hasMoreTokens()) {
              Integer i = new Integer(tok.nextToken());
              size=i.intValue();
              if(tok.hasMoreTokens()) {
                Double d = new Double(tok.nextToken());
                umbra=d.doubleValue();
              }
            }
          }
          pn.initiateNode(nodename,size,umbra,region);
          rm.addRegion(nodename,Symbol.GetSymbol("PNb"),region,
                       new NodeRegion(nodename));
        } else if(cmd==killCmd) {
          if(tok.hasMoreTokens()) {
            Object nodename=Symbol.GetSymbol(tok.nextToken());
            pn.killNode(nodename);
          } else {
            Iterator i = pn.getNodes().iterator();
            while(i.hasNext()) { pn.killNode(i.next()); }
          }
        } else if(cmd==getCmd) {
          if(tok.hasMoreTokens()) {
            Object nodename=Symbol.GetSymbol(tok.nextToken());
            Object d = pn.getData(nodename);
            pendingAck.add(new Pair(new Integer(time+18),"Value stored in "+
                                    nodename+" at "+pp.getUID()+": "+d));
          }
        } else if(cmd==setCmd) {
          if(tok.hasMoreTokens()) {
            Object nodename=Symbol.GetSymbol(tok.nextToken());
            if(tok.hasMoreTokens()) {
              Object value=Symbol.GetSymbol(tok.nextToken());
              DelayedReturn dr = new DelayedReturn.Boolean();
              dr.addObserver(this);
              pn.setData(nodename,value,dr);
            }
          }
        }
      }
    } else if(name==PortedProcessor.kClock) {
      int t = ((Integer)data).intValue();
      time=t;

      // do delayed acks:
      Iterator ackit = pendingAck.iterator();
      while(ackit.hasNext()) {
        Pair p = (Pair)ackit.next();
        int ackTime = ((Integer)p.car()).intValue();
        if(ackTime<=time) {
          System.out.println(time+": "+p.cdr());
          ackit.remove();
        }
      }

      // now we try the silly population thing...
      if(false) {
        Vector nodes = pn.getNodes();
        Vector reflectors = pn.getNodeReflectors();
        Vector umbras = pn.getNodeUmbras();
        Vector penumbras = pn.getNodePenumbras(2.5);
        if(nodes.size()>1) { // if we're in two nodes, kill lesser
          Iterator i=nodes.iterator(); Double best=null;
          while(i.hasNext()) {
            Double nid = (Double)i.next();
            if(best==null) { best=nid; }
            else if(best.compareTo(nid)<0) {
              pn.killNode(best); best=nid;
            } else {
              pn.killNode(nid);
            }
          }
        } else if(penumbras.isEmpty()) { // if we're in none, start one
          if(Math.random()<0.00289) { // f=0.00002
            Double nid = new Double(Math.random());
            pn.initiateNode(nid,2,3,new NodeExclusionEstimator(nid),RegionManager.rootID); //r=16
          }
        }
      }

      Gradient g1 = ag.get(test);
      Gradient g2 = ag.get(test2);
      Set pnv = ag.getType(pn.pnType);
      Vector nodes = pn.getNodes();

      // coloring: none=blue; live=red or green; dead=magenta; init=yellow
      boolean live = false;
      Iterator i = pnv.iterator();
      while(i.hasNext()) { if(!(ag.get(i.next()).isDead())) live=true; }
      boolean node1 = nodes.contains(test);
      boolean node2 = nodes.contains(test2);
      boolean pending=false; boolean backprop=false;
      i = nodes.iterator();
      while(i.hasNext()) { 
        int v = pn.debugit(i.next());
        if(v%2==1) pending=true;
        if((v%4)/2==1) backprop=true;
      }
      
      boolean falsecolor=false;
      if(falsecolor) {
        //if(nodes.size()>0) cm.flash();
        if(nodes.size()==0) cm.setColor(Color.gray);
        else {
          int closest=-1; Object best = null;
          i = nodes.iterator();
          while(i.hasNext()) {
            Object nid = i.next();
            Gradient g = ag.get(nid);
            if(!g.isDead() && (closest==-1 || g.getHopcount()<closest)) {
              best=g.gid; closest=g.getHopcount();
            }
          }
          if(best!=null) {
            int n = 0;
            if(best instanceof Double)
              n = (int)(((Double)best).doubleValue()*10000);
            else n = best.hashCode();
            cm.setColor(cm.numberToColor(n));
          } else cm.setColor(Color.gray);
        }
      } else {
        if(pnv.size()==0) cm.setColor(Color.blue);
        else if(nodes.size()>1) cm.setColor(Color.white);
        else if(backprop) cm.setColor(Color.green);
        else if(pending) cm.setColor(Color.red);
        else if(!node1&&!node2 && nodes.size()>0) cm.setColor(Color.lightGray);
        else if(live) cm.setColor(Color.magenta);
        else cm.setColor(Color.cyan);
        i = pnv.iterator();
        while(i.hasNext()) {
          Object nid = i.next();
          if(ag.get(nid).getHopcount()==0) {
            if(nodes.contains(nid))
              cm.setColor(Color.yellow);
            else 
              cm.setColor(Color.orange); 
          }
        }
      }
      if(false) {
        Vector n = pn.getNodes();
        Vector reflectors = pn.getNodeReflectors();
        Vector umbras = pn.getNodeUmbras();
        if(n.size()>0)
          cm.setColor(Color.red);
        else if(reflectors.size()>0)
          cm.setColor(Color.blue);
        else if(umbras.size()>0)
          cm.setColor(Color.green);
        else
          cm.setColor(Color.gray);
      }
    }
    return dirty;
  }

  public String toString() {
    return "PNTestProc sez we're in "+pn.getNodePenumbras(2.5);
  }
}


