package testprocs;

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

class DataflowProcFactory implements ProcessorFactory {
  public Processor MakeProcessor(Simulator s, double x, double y) {
    PortedProcessor pp = new PortedProcessor(s); // base function
    // infrastructure
    pp.addModule(new ColorModule());
    pp.addModule(new ActiveGradient()); 
    pp.addModule(new NeighborSet());
    // graph formation
    //pp.addModule(new CodeGas()); // ready
    pp.addModule(new PersistentNodeB());
    pp.addModule(new PersistentLineB());
    //pp.addModule(new GraphBuilder());
    // code execution
    pp.addModule(new DataflowInterpreter());
    // top level tagout class
    pp.addModule(new DataflowProc());
    return pp;
  }
}

public class DataflowProc implements ProcessorModule {
  public Symbol getName() { return Symbol.GetSymbol("DataflowProc"); }
  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

  public static double initiator = Double.NaN;
  static Symbol line = Symbol.GetSymbol("line");
  public void init() {
    if(!initted) { initted=true; H(); makeNodeTable(); } // load the cheatnodes
  }

  public static Hashtable nodetable = new Hashtable();
  void makeNodeTable() { // make hashtable to name->function
    Iterator i = cheatNodes.iterator();
    while(i.hasNext()) {
      Object o = i.next();
      if(!(o instanceof DFNode)) continue;
      DFNode df = (DFNode)o;
      nodetable.put(df.getName(),df);
    }
    DataflowInterpreter.nodetable=nodetable;
  }
  void startNextCheat() {
    if(!cheatNodes.isEmpty()) { // pull off first cheatnode and construct
      if(cheatNodes.firstElement() instanceof DFNode) {
        DFNode df = (DFNode)cheatNodes.firstElement();
        cheatNodes.remove(df);
        // make node
        pn.initiateNode(df.getName());
        Iterator i = df.getPorts();
        while(i.hasNext()) {
          DFNPort port = (DFNPort)i.next();
          LineAnchor la1 = new NodeAnchor(df.getName());
          Vector l = new Vector(Arrays.asList(port.out()));
          //l.addAll(Arrays.asList(port.in()));
          Iterator j = l.iterator();
          while(j.hasNext()) {
            Object end = j.next();
            LineAnchor la2 = new NodeAnchor(end);
            pl.initiateLine(new Pair(line,new Pair(df.getName(),end)),la1,la2);
          }
        }
      } else {
        DFNLambda.init=cheatNodes.firstElement();
        System.out.println("Function called with value: "+DFNLambda.init);
        cheatNodes.remove(0);
      }
    }
  }

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

      if(initiator==pp.getUID()) { initiator=Double.NaN; startNextCheat(); }

      if(!report && checkResult()) { report=true; }

      Vector nodes = pn.getNodes();
      Vector lines = pl.getLines();
      Set calls = ag.getType(DFNode.call);
      boolean isdata = false;
      Iterator i=calls.iterator(); 
      while(i.hasNext()) { 
        Gradient g=(Gradient)i.next(); 
        if(!g.isDead()) isdata=true; 
      }

      if(false) {}
      else if(nodes.size()>0) cm.setColor(nodeColor(nodes.get(0)));
      else if(isdata) cm.setColor(Color.magenta);
      else if(lines.size()>0) cm.setColor(Color.white);
      else cm.setColor(Color.blue);
    }
    return dirty;
  }

  Color nodeColor(Object name) {
    //System.out.println(name+" "+nodetable);
    Object n = nodetable.get(name);
    if(false) { return Color.blue; }
    else if(n instanceof DFNLambda) { return Color.green; }
    else if(n instanceof DFNConstant) { return Color.orange; }
    else if(n instanceof DFNVariable) { return Color.red; }
    else if(n instanceof DFNOperator) { return Color.cyan; }
    //else if(n instanceof DFNCall) { return Color.magenta; }
    //else if(n instanceof DFNIf) { return Color.yellow; }
    else {
      System.out.println("unknown node type: "+n);
      return Color.blue;
    }
  }

  Object nodeIDsrc(Object gid) { return ((Pair)((Pair)gid).car()).car(); }
  Object nodeIDdst(Object gid) { return ((Pair)((Pair)gid).car()).cdr(); }
  boolean checkResult() {
    // find a lambda node
    DFNode n = null;
    Iterator i=pn.getNodes().iterator();
    while(i.hasNext()) { 
      Object o = nodetable.get(i.next());
      if(o!=null && (o instanceof DFNLambda)) { n=(DFNode)o; break; }
    }
    if(n==null) return false;
    // check if its result has occurred
    Set calls = ag.getType(DFNode.call);
    i=calls.iterator();
    while(i.hasNext()) {
      Gradient g = (Gradient)i.next();
      if(nodeIDdst(g.gid).equals(endID)) {
        System.out.println("Function returns value: "+g.data);
        return true;
      }
    }
    return false;
  }

  static boolean initted=false;
  static Vector cheatNodes = new Vector();
  static Object endID=null;
  public void H() {
    double[] xxx = new double[]{};
    Object[] yyy = new Object[]{};
    Symbol h = Symbol.GetSymbol("h");
    Symbol io = Symbol.GetSymbol("io");
    Symbol plus = Symbol.GetSymbol("plus");
    
    Vector l=cheatNodes;
    l.add(new DFNLambda
      (480375,h,yyy,new DFNPort[]{new DFNPort(DFNode._return,123225,xxx),
                                  new DFNPort(DFNode._nvl,xxx,xxx),
                                  new DFNPort(DFNode._activate,xxx,48408)}));
    l.add(new DFNVariable(48408, DFNode._activate, 
                          new DFNPort(io,480375,new double[]{611840,804668})));
    l.add(new DFNConstant(611840, new Integer(3), 
                          new DFNPort(io,48408,123225)));
    l.add(new DFNConstant(804668, new Integer(4),
                          new DFNPort(io,48408,123225)));
    l.add(new DFNOperator(123225, plus,
                          new DFNPort(io,new double[]{611840,804668},480375)));
    l.add(DFNode._activate);
    endID = new Double(480375);
  }
  

  public void G() {
    double[] xxx = new double[]{};
    Object[] yyy = new Object[]{};
    Symbol g = Symbol.GetSymbol("g");
    Symbol x = Symbol.GetSymbol("x");
    Symbol io = Symbol.GetSymbol("io");
    Symbol times = Symbol.GetSymbol("times");
    
    Vector l=cheatNodes;
    l.add(new DFNLambda
      (733233,g,yyy,new DFNPort[]{new DFNPort(DFNode._return,857566,xxx),
                                  new DFNPort(x,xxx,337476)}));
    l.add(new DFNVariable(337476, x, 
                         new DFNPort(io,733233,new double[]{857566,857566})));
    l.add(new DFNOperator(857566, times,
                          new DFNPort(io,new double[]{337476,337476},733233)));
    l.add(new Double(2));
    endID = new Double(733233);
  }
}
