package testprocs;

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

class MyProcFactory implements ProcessorFactory {
  public Processor MakeProcessor(Simulator s, double x, double y) {
    PortedProcessor pp = new PortedProcessor(s);
    pp.addModule(new ColorModule());
    pp.addModule(new ActiveGradient());
    pp.addModule(new NeighborSet());
    //pp.addModule(new CodeGas());
    pp.addModule(new PersistentNodeB());
    //pp.addModule(new PersistentLineB());
    pp.addModule(new PNTestProc());
    return pp;
  }
}

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


  static final Symbol testPort = Symbol.GetSymbol("test");
  PortedProcessor pp;
  ActiveGradient ag; ColorModule cm; NeighborSet ns;
  CodeGas cg;

  static final Symbol test = Symbol.GetSymbol("test");
  static final Symbol test2 = Symbol.GetSymbol("test2");
  static final Symbol gin = Symbol.GetSymbol("g-in");
  static final PropagationTest dp = new DistancePropagation(10); // 10
  static final PropagationTest git = 
    new GradientInternalPropagation(new Object[]{test});
  public static double initiator = Double.NaN;
  static double initiator2 = 0;
  public void init() {
    if(initiator==0 || Double.isNaN(initiator)) { 
      initiator = pp.getUID();
    } else if(initiator2==0) {
      initiator2 = pp.getUID();
    }
    pv.name = pp.getUID();
  }

  // hunting for edges
  PV pv = new PV();
  public static int lastMove = 0;
  
  public boolean signalEvent(Symbol name,Object data) {
    boolean dirty = false;
    if(name==PortedProcessor.kClock) {
      int t = ((Integer)data).intValue();
      if(initiator==pp.getUID()) {
        ag.sendGradient(test,test,null,dp);
        //if(t-lastMove>500) { // anoint a successor
        if(t-lastMove>50) { // bouncing ball successor?
          System.out.println("Transfer!");
          lastMove=t;
          Iterator i=pv.successors.iterator();
          double best = -10;
          while(i.hasNext()) {
            PV spv = (PV)i.next();
            if(spv.value > best) { best = spv.value; initiator=spv.name; }
          }
        }
      }
      Gradient g = ag.get(test);

      // functionality:
      // gradient goes out. Then everybody calculates some stuff.
      // (n predecessors, value, value-to-neighbors)
      if(t%5==0) { pp.sendMessage(testPort, pv); }
      if(g==null || g.isDead()) pv.hop = Double.NaN;
      else { 
        if(Double.isNaN(pv.hop)) {
          pv.predecessors.clear();
          pv.successors.clear();
          pv.others.clear();
          if(g.isEdge()) pv.value = 1.0; else pv.value = 0.0;
        }
        pv.hop = g.getHopcount();
      }

      // coloring
      if(g==null) {
        cm.setColor(Color.blue);
      } else if(g.isFinalDead()) {
        cm.setColor(Color.magenta); 
      } else if(g.isDead()) {
        cm.setColor(Color.green);
      } else {
        if(pv.isMax()) {
          cm.setColor(Color.cyan);
        } else if(g.isEdge()) {
          cm.setColor(Color.red);
        } else {
          cm.setColor(Color.white);
        }
      }
      if(initiator==pp.getUID()) cm.setColor(Color.yellow); 

    } else if(name==testPort) {
      PV nbrpv = (PV)data;
      if(!Double.isNaN(nbrpv.name) && !Double.isNaN(nbrpv.hop)) {
        // prune existing:
        Iterator i;
        i=pv.predecessors.iterator();
        while(i.hasNext()) 
          { PV n = (PV)i.next(); if(n.hop != pv.hop-1) i.remove(); }
        i=pv.successors.iterator();
        while(i.hasNext()) 
          { PV n = (PV)i.next(); if(n.hop != pv.hop+1) i.remove(); }
        i=pv.others.iterator();
        while(i.hasNext()) 
          { PV n = (PV)i.next(); if(n.hop == pv.hop-1 || n.hop==pv.hop+1) 
            i.remove(); }

        // add new
        if(nbrpv.hop == pv.hop+1) { // if successor:
          if(!pv.successors.contains(nbrpv)) { pv.successors.add(nbrpv); }
        } else if(nbrpv.hop == pv.hop-1) { // if predecessor
          if(!pv.predecessors.contains(nbrpv)) { pv.predecessors.add(nbrpv); }
        } else {
          if(!pv.others.contains(nbrpv)) { pv.others.add(nbrpv); }
        }
        // recalculate value
        if(pv.successors.size()==0) {
          pv.value = 1;
        } else {
          pv.value = 1; // actually, we'll have processors add themselves
          i = pv.successors.iterator();
          while(i.hasNext()) {
            PV spv = (PV)i.next();
            pv.value += spv.value;///spv.predecessors.size();
          }
        }
        // recalculate value2
        pv.value2 = 0;
        i=pv.predecessors.iterator();
        while(i.hasNext()) { pv.value2 += ((PV)i.next()).value; }
        i=pv.successors.iterator();
        while(i.hasNext()) { pv.value2 += ((PV)i.next()).value; }
        i=pv.others.iterator();
        while(i.hasNext()) { pv.value2 += ((PV)i.next()).value; }
        pv.value2 /= (pv.predecessors.size()+pv.successors.size()+
                      pv.others.size());
      }
    }
    return dirty;
  }

  public String toString() {
    return pv.toString();
  }

  /*
  // diffusion example
  static double gamma = 0.01;
  static double k = 1;
  static double alpha = 100;
  static double delta = 0.1;
  double stock = 0;
  public boolean signalEvent(Symbol name,Object data) {
    boolean dirty = false;
    if(name==PortedProcessor.kClock) {
      int t = ((Integer)data).intValue();
      if(t%4==0) {
        if(initiator==pp.getUID()) {
          stock += k;
        }
        // lose some of our own stock
        double decay = stock*gamma;
        stock -= decay;
        // then transmit stock to others
        if(stock>0) {
          double diffuse = (stock*delta);//alpha;
          if(diffuse > stock) diffuse = stock;
          double drop = diffuse/ns.size();
          stock -= diffuse;
          pp.sendMessage(testPort,new Double(drop));
        }
      }
      cm.setColor(cm.numberToColor((int)(stock+0.5)));
    } else if(name==testPort) {
      double rise = ((Double)data).doubleValue();
      stock += rise;
    }
    return dirty;
  }
  */
}

class PV {
  Vector predecessors = new Vector();
  Vector successors = new Vector();
  Vector others = new Vector();
  double value = 0.0;
  double value2 = 0.0;
  double name = Double.NaN;
  double hop = Double.NaN;

  boolean isMax() {
    Iterator i;
    i=predecessors.iterator();
    while(i.hasNext()) { if(((PV)i.next()).value > value) return false; }
    i=successors.iterator();
    while(i.hasNext()) { if(((PV)i.next()).value > value) return false; }
    i=others.iterator();
    while(i.hasNext()) { if(((PV)i.next()).value > value) return false; }
    return true;
  }
  public String toString() {
    String s = "";
    s+="("+predecessors.size()+", "+successors.size()+", "+others.size()+", "+value+", "+value2+", "+name+", "+hop+")";
    return s;
  }
}





// Code Gas testing:

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


  static final Symbol testPort = Symbol.GetSymbol("test");
  PortedProcessor pp;
  ActiveGradient ag; ColorModule cm; NeighborSet ns;
  CodeGas cg;

  static final Symbol test = Symbol.GetSymbol("test");
  static final Symbol test2 = Symbol.GetSymbol("test2");
  static final PropagationTest dp = new DistancePropagation(10); // 10
  static double initiator = 0;
  static double initiator2 = 0;
  public void init() {
    if(initiator==0) { 
      initiator = pp.getUID();
    } else if(initiator2==0) {
      initiator2 = pp.getUID();
    }
  }

  static Vector savedata = new Vector();
  static double estimate = 0;
  static int count = 0;
  public boolean signalEvent(Symbol name,Object data) {
    boolean dirty = false;
    if(name==PortedProcessor.kClock) {
      int t = ((Integer)data).intValue();
      //if(initiator==pp.getUID()) {
      //  ag.sendGradient(test,test,null,dp);
      //}
      if(t%10==0 && t<100 && initiator==pp.getUID()) {
        cg.addParticle(test);
      }
      if(t%10==0 && t<100 && initiator2==pp.getUID()) {
        cg.addParticle(test2);
      }
      // set fixative appropriately:
      //Gradient g = ag.get(test);
      //if(g==null) { cg.unfixParticle(test); cg.unfixParticle(test2); }
      //else { cg.fixParticle(test); cg.fixParticle(test2); }
      
      // data-gathering phase
      if(t>=3000 &&initiator==pp.getUID()) {
        if(t%50==0) {
          estimate = (estimate*count+cg.globalCount)/(count+1);
          count++;
        }
        //if(t<4000) {
        //  if(t%50==0) {
        //    savedata.add(new Integer(cg.globalCount));
        //  }
        //} else {
        //  if(estimate==0) {
        //    Iterator i = savedata.iterator();
        //    while(i.hasNext()) {
        //      estimate += ((Integer)i.next()).doubleValue()/20;
        //    }
        //  }
        //}
      }
      //cm.setColor(cm.numberToColor(cg.countParticles()-1));
      Vector v = cg.getParticles();
      boolean t1 = v.contains(test);
      boolean t2 = v.contains(test2);
      if(t1&&t2) { cm.setColor(Color.white); }
      else if(t1) { cm.setColor(Color.green); }
      else if(t2) { cm.setColor(Color.red); }
      else { cm.setColor(Color.blue); }
    }
    return dirty;
  }

  public String toString() {
    return "Final Estimate: "+estimate+"\n(From: "+savedata.toString()+")";
  }
}

// Persistent Line testing

class PLTestProc implements ProcessorModule {
  public Symbol getName() { return Symbol.GetSymbol("MyProc"); }
  public void link(PortedProcessor p) {
    pp = p;
    pp.addClockListener(this);
    ag = (ActiveGradient)p.getModule("ActiveGradient");
    cm = (ColorModule)p.getModule("Color");
    pn = (PersistentNodeB)p.getModule("PersistentNode");
    pl = (PersistentLineB)p.getModule("PersistentLine");
  }

  PortedProcessor pp; ActiveGradient ag; ColorModule cm; PersistentNodeB pn;
  PersistentLineB pl; // use type B instead

  static final Symbol test = Symbol.GetSymbol("test");
  static final Symbol test2 = Symbol.GetSymbol("test2");
  static final LineAnchor la = new NodeAnchor(test);
  static final LineAnchor la2 = new NodeAnchor(test2);
  static final Symbol line = Symbol.GetSymbol("line");

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

  public boolean signalEvent(Symbol name,Object data) {
    boolean dirty = false;
    if(name==PortedProcessor.kClock) {
      int t = ((Integer)data).intValue();

      Set pnv = ag.getType(pn.pnType);
      Set plvS = ag.getType(pl.searchType);
      Vector nodes = pn.getNodes();
      Vector lines = pl.getLines();

      // coloring: none=blue; live=red or green; dead=magenta; init=yellow
      boolean live = false;
      Iterator i = plvS.iterator();
      while(i.hasNext()) { if(!((Gradient)i.next()).isDead()) live=true; }
      boolean node1 = nodes.contains(test);
      boolean node2 = nodes.contains(test2);
      Gradient fill = ag.get(line);
      boolean backbone = pl.inLine();
      //boolean backbone = (fill!=null && !fill.isDead() && fill.getHopcount()==0);

      if(false) {}
      else if(backbone) cm.setColor(Color.green);
      else if(lines.size()>0) cm.setColor(Color.white);
      else if(node1 || node2) cm.setColor(Color.red);
      else if(live) cm.setColor(Color.magenta);
      else if(plvS.size()>0) cm.setColor(Color.cyan);
      else cm.setColor(Color.blue);
      if(la.member(pp) || la2.member(pp))
        cm.setColor(Color.yellow);

      Gradient gn = ag.get(new Pair(line,la));
      //if(gn!=null)
      //  cm.setColor(cm.numberToColor(gn.getHopcount()));
    }
    return dirty;
  }

  public String toString() {
    return "";
  }
}
