package testprocs;

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

// Persistent Node testing

public class RegionAlpha implements ProcessorModule {
  public Symbol getName() { return Symbol.GetSymbol("RMTestProc"); }
  public void link(PortedProcessor p) {
    pp = p;
    pp.addClockListener(this);
    pp.openNetworkPort(this,uiPort);
    pp.openNetworkPort(this,type);
    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 type = Symbol.GetSymbol("Rv");

  public void init() {
  }

  static int count = 0;
  static double probeID = Double.NaN;
  static double sinkID = Double.NaN;
  static double estimate = 0;
  static double influx = 1.0;
  static double k_d = 0.95; // diffusion constant
  static DTEstimator flux = new ExponentialBackoffFilter(0,0.5);
  static DTEstimator netflux = new ExponentialBackoffFilter(0,0.1);
  
  boolean inregion = false;
  double value = 0;
  double oldvalue = 0;

  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("region")) {
          if(tok.hasMoreTokens() && tok.nextToken().equals("none")) {
            if(inregion) count--;
            inregion=false;
          } else {
            if(!inregion) count++;
            inregion=true;
            if(Double.isNaN(sinkID)) sinkID=pp.getUID();
          }
        } else if(cmd.equals("probe")) {
          if(tok.hasMoreTokens() && tok.nextToken().equals("off")) {
            probeID = Double.NaN;
          } else {
            probeID = pp.getUID();
          }
        } else if(cmd.equals("sink")) {
          if(inregion) sinkID=pp.getUID();
        }
      }
    } else if(name==type) {
      Pair p = (Pair)data;
      if(((Double)p.car()).doubleValue()==pp.getUID()) {
        value += ((Double)p.cdr()).doubleValue();
      }
    } else if(name==PortedProcessor.kClock) {
      int t = ((Integer)data).intValue();
      if(pp.getUID()==probeID) {
        System.out.println(" Estimate: "+estimate+" Actual: "+count);
      }

      if(inregion) {
        value+=influx; // add source value
        if(sinkID==pp.getUID()) {
        }
        int nn=0;
        Iterator i = ns.getNeighbors(); // diffusion via neighborset
        while(i.hasNext()) {
          Double nbrdata = (Double)ns.getData(type,i.next());
          if(nbrdata!=null) { nn++; }
        }
        double total_delta = 0;
        i=ns.getNeighbors();
        while(i.hasNext()) {
          Object id = i.next();
          Double nbrdata = (Double)ns.getData(type,id);
          if(nbrdata!=null) { 
            double delta = (nbrdata.doubleValue()-value)*(k_d/nn);
            total_delta+=delta;
            pp.sendMessage(type,new Pair(id,new Double(-delta)));
          } 
        }
        value += total_delta;
        // maintain records
        if(sinkID==pp.getUID()) {
          flux.update(value-oldvalue); // should be 1st deriv
          value-=flux.value();
          netflux.update(value-oldvalue); // should be 2nd deriv
          estimate = flux.value();
        }
        ns.setData(type,new Double(value));
        oldvalue=value;
      } else {
        value = 0;
        ns.setData(type,null);
      }

      // coloring: red sink, green source
      if(!inregion) cm.setColor(Color.blue); 
      else if(sinkID==pp.getUID()) cm.setColor(Color.red);
      else cm.setColor(Color.green);
    }
    return dirty;
  }

  public String toString() {
    return "RegionAlpha: "+inregion+", "+value+", "+flux+", "+netflux+
      ", "+count;
  }
}


