package testprocs;

import simulator.*;
import scripting.*;
import modules.color.*;
import modules.neighborset.*;
import modules.gradient.*;
import utils.*;
import java.util.*;
import java.awt.*;

public class Router implements ProcessorModule {
  public Symbol getName() { return Symbol.GetSymbol("Router"); }
  public void link(PortedProcessor p) {
    pp = p;
    pp.addClockListener(this);
    pp.openNetworkPort(this,uiPort);
    pp.openNetworkPort(this,comm);
    cm = (ColorModule)p.getModule("Color");
    ns = (NeighborSet)p.getModule("NeighborSet");
    ag = (ActiveGradient)p.getModule("ActiveGradient");
  }


  PortedProcessor pp;
  ColorModule cm; NeighborSet ns; ActiveGradient ag;

  static final Symbol uiPort = Symbol.GetSymbol("UI");
  static final Symbol comm = Symbol.GetSymbol("comm");
  static final Symbol gid = Symbol.GetSymbol("gid");

  static double source = Double.NaN;
  static double sink = Double.NaN;
  public void init() {
    if(Double.isNaN(source)) {
      source = pp.getUID();
    } else if(Double.isNaN(sink)) {
      sink = pp.getUID();
    }
  }
  
  static int totalpackets = 0;
  int localpackets = 0;
  static boolean showpackets = false;
  
  static class Packet {
    static int n=0;
    int id = n++;
    public String toString() { return "Packet "+id; }
  }
  static double p_forward = 0.7;

  Random rnd = new Random();
  Vector packets = new Vector();
  static Counter timeout = new Counter(10);
  public boolean signalEvent(Symbol name,Object data) {
    boolean dirty = false;
    if(name==uiPort) {
      StringTokenizer tok = new StringTokenizer(data.toString());
      if(tok.hasMoreTokens()) { 
        String cmd=tok.nextToken();
        if(cmd.equals("spray")) {
          double v = 0;
          if(tok.hasMoreTokens())v=(new Double(tok.nextToken())).doubleValue();
          p_forward=1-v;
          System.out.println("Spray factor set to "+v);
        } else if(cmd.equals("source")) {
          source=pp.getUID();
        } else if(cmd.equals("sink")) {
          sink=pp.getUID();
        } else if(cmd.equals("packets")) {
          String s = null;
          if(tok.hasMoreTokens()) s=tok.nextToken();
          if(s==null) showpackets = !showpackets;
          else if(s.equals("show")) { showpackets=true; }
          else if(s.equals("hide")) { showpackets=false; }
          System.out.println("Packet visibility set to "+showpackets);
        } else if(cmd.equals("reset")) {
          timeout.reset();
          System.out.println("Resetting counts.");
        }
      }
    } else if(name==PortedProcessor.kClock) {
      Gradient g = ag.get(gid);
      if(g==null) ns.setData(comm,null); 
      else ns.setData(comm,new Integer(g.getHopcount()));

      // source originates packets
      if(source==pp.getUID()) {
        if(g!=null && timeout.expired()) {
          if(Math.random()<1.0) {
            Packet packet = new Packet();
            packets.add(packet); localpackets++; totalpackets++;
            System.out.println("Emit "+packet.toString());
          }
        }
      }

      boolean haspackets = !packets.isEmpty();
      
      if(!timeout.expired()) {
        localpackets=0; packets.clear();
      }

      // sink destroys packets
      if(sink==pp.getUID()) {
        if(!timeout.expired()) {
          timeout.tick(); totalpackets=0;
        }

        ag.sendGradient(gid, comm, null);
        if(!packets.isEmpty()) {
          System.out.println("Killing "+packets);
          packets.clear();
        }
      }
      
      // forward packets to neighbors or sink
      boolean sidestep = false;
      if(g!=null) {
        Iterator i = packets.iterator();
        while(i.hasNext()) {
          Packet p = (Packet)i.next();
          Object target = null;
          if(Math.random()<p_forward) {
            // pick a neighbor with a better hopcount
            Vector candidates = new Vector();
            Iterator j = ns.getNeighbors();
            while(j.hasNext()) {
              target = j.next();
              Integer nbrhop = (Integer)ns.getData(comm,target);
              if(nbrhop!=null && g.getHopcount()>nbrhop.intValue())
                candidates.add(target);
            }
            if(!candidates.isEmpty())
              target = candidates.get(rnd.nextInt(candidates.size()));
          } else if(sidestep) {
            // pick a neighbor with an equal hopcount
            Vector candidates = new Vector();
            Iterator j = ns.getNeighbors();
            while(j.hasNext()) {
              target = j.next();
              Integer nbrhop = (Integer)ns.getData(comm,target);
              if(nbrhop!=null && g.getHopcount()==nbrhop.intValue())
                candidates.add(target);
            }
            if(!candidates.isEmpty())
              target = candidates.get(rnd.nextInt(candidates.size()));
          } 
          if(target==null) {
            // pick a neighbor at random
            target = new Double(ns.getRandomNeighbor(false));
          }
          pp.sendMessage(comm,new Pair(target,p));
          i.remove();
        }
      }

      // last but not least, color ourselves
      double value = 0;
      if(totalpackets>0) {
        value=Math.log(256*localpackets/totalpackets)/Math.log(2);
        //value+=5; 
        if(value<0) value=0;
        value/=10.0; mycolor=value;
      }
      //int value = 0; if(totalpackets>0) value=localpackets*5/totalpackets;
      if(showpackets&&haspackets) cm.setColor(Color.yellow);
      else if(pp.getUID()==source) cm.setColor(Color.red);
      else if(pp.getUID()==sink) cm.setColor(Color.magenta);
      else {
        //cm.setColor(cm.numberToColor(((int)value)-1));
        cm.setColor(Color.getHSBColor((float)(0.666-value),(float)1.0,(float)1.0));
      }
    } else if(name==comm) {
      Object id = ((Pair)data).car();
      Object packet = ((Pair)data).cdr();
      if(id.equals(pp.getUIDd())) {
        packets.add(packet); localpackets++;
      }
    }
    return false;
  }

  double mycolor;
  public String toString() { 
    return "Traffic: "+localpackets+" of "+totalpackets+
      " (Currently: "+packets+") Traffic Rating="+mycolor;
  }
}

