package testprocs;

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

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


  PortedProcessor pp;
  ColorModule cm; NeighborSet ns;

  static final Symbol uiPort = Symbol.GetSymbol("UI");
  static final Symbol inkPort = Symbol.GetSymbol("ink");

  public void init() {
    ns.setData(inkPort,data.clone());
    clean.set(5);
  }

  static int dots=0;
  boolean dot=false;
  static boolean backup = true;

  InkData data=new InkData();
  Counter c = new Counter(10);
  static Counter clean = new Counter(5);
  static double cleaner = Double.NaN;
  static Random r = new Random();

  public boolean signalEvent(Symbol name,Object evtData) {
    boolean dirty = false;
    if(name==uiPort) {
      StringTokenizer tok = new StringTokenizer(evtData.toString());
      if(tok.hasMoreTokens()) { 
        String cmd=tok.nextToken();
        if(cmd.equals("clean")) {
          cleaner=pp.getUID(); clean.reset();
        } else if(cmd.equals("backup")) {
          backup=!backup; System.out.println("Backup set to: "+backup);
        } else if(cmd.equals("ink")) {
          if(tok.hasMoreTokens()) 
            data.ink += (new Integer(tok.nextToken())).intValue();
          else
            data.ink += 200;
          if(data.hop==-1) data.hop=0;
          ns.setData(inkPort,data.clone());
          c.reset();
        }
      }
    } else if(name==PortedProcessor.kClock) {
      if(!clean.expired()) {
        data.kids=true; data.ink=0; data.hop=-1; ns.setData(inkPort,data.clone()); c.reset();
        if(cleaner==pp.getUID()) {
          clean.tick();
          if(clean.expired())
            System.out.println("Cleaning done");
        }
      } else if(data.ink>0) {
        if(data.ink<1) { data.hop=-1; data.kids=true; }
        c.tick();
        if(c.expired()) { // send ink extras
          c.reset();
          // the rules: if there are any kids, give it to them.
          // if no kids, set kid-ness to false and hand it to parent
          // hop is set by incoming tuples
          Vector nbrs = ns.getNeighborList();
          Vector kids = new Vector();
          Vector parents = new Vector();
          Iterator i = nbrs.iterator();
          while(i.hasNext()) {
            Object nid = i.next();
            InkData ni = (InkData)ns.getData(inkPort,nid);
            if(ni==null) continue;
            if((ni.hop==-1 || ni.hop>data.hop) && ni.kids)
              kids.add(nid);
            if(ni.hop<data.hop) parents.add(nid);
          }

          if(kids.size()>0) {
            data.kids=true;
            while(kids.size()>0 && data.ink>1) {
              Object nid = kids.remove(r.nextInt(kids.size()));
              int delta = (data.ink-1)/(1+kids.size());
              data.ink -=delta;
              if(delta>0)
                pp.sendMessage(inkPort,new Tuple(nid,new Integer(delta),new Integer(data.hop)));
            }
          } else if(backup) {
            data.kids=false;
            while(parents.size()>0 && data.ink>1) {
              Object nid = parents.remove(r.nextInt(parents.size()));
              int delta = (data.ink-1)/(1+parents.size());
              data.ink -=delta;
              if(delta>0)
                pp.sendMessage(inkPort,new Tuple(nid,new Integer(delta),new Integer(data.hop)));
            }
          }
          ns.setData(inkPort,data.clone());
        }
      }
      
      // coloring
      if(data.ink > 1) cm.setColor(Color.red);
      else if(data.ink == 1 && data.kids) cm.setColor(Color.cyan);
      else if(data.ink == 1) cm.setColor(Color.yellow);
      else if(data.ink == 0) cm.setColor(Color.blue);
      else cm.setColor(Color.white); // shouldn't happen!
    } else if(name==inkPort) {
      Tuple t = (Tuple)evtData;
      if(pp.getUID()==((Double)t.first()).doubleValue()) {
        data.ink += ((Integer)t.second()).intValue();
        int hop = ((Integer)t.third()).intValue();
        if(data.hop==-1 || data.hop>hop+1) data.hop=hop+1;
        ns.setData(inkPort,data.clone());
        c.reset();
      }
    }
    if(dot && data.ink<1) { dot=false; dots--; }
    else if(!dot && data.ink>0) { dot=true; dots++; }

    return dirty;
  }

  public String toString() { 
    return "Ink: "+data.ink+" / "+dots+" ("+data.kids+", "+data.hop+")";
  }
}

class InkData {
  int ink=0;
  boolean kids=true;
  int hop=-1;

  public Object clone() {
    InkData d = new InkData();
    d.ink=ink; d.kids=kids; d.hop=hop;
    return d;
  }
}
