package simulator;

import utils.*;
import java.util.*;
import java.awt.*;

// Functions provided by the ported processor:
// 1. a clock with ticking events
// 2. ported messaging services
// 3. module registry for internal structuring
public class PortedProcessor extends Processor {

  public PortedProcessor(Simulator sim) { 
    super(sim);
  }

  public String toString() {
    String s = "PP "+getUID();
    Iterator i = modules.values().iterator();
    while(i.hasNext()) {
      s += "\n" + i.next().toString();
    }
    return s;
  }

  // first link, then init
  public void init() {
    Iterator i = modules.values().iterator();
    while(i.hasNext()) {
      ProcessorModule pm = (ProcessorModule)i.next();
      pm.link(this);
    }
    i = modules.values().iterator();
    while(i.hasNext()) {
      ProcessorModule pm = (ProcessorModule)i.next();
      pm.init();
    }
    // now initialize the clock and transmitter systems
    timer(1,kClock);
    timer(0.5+2*Math.random(),kTransmit);
    initted = true;
  }
  private boolean initted = false;

  public static final Symbol kClock = Symbol.GetSymbol("tick");
  public static final Symbol kTransmit = Symbol.GetSymbol("xmit");
  int time = 0;
  public void signalEvent(Symbol name,Object data) {
    if(!initted) return; // make sure we're initted before accepting events

    if(name == Simulator.kMessage) {
      // dispatch received message
      Symbol port = (Symbol)((Pair)data).car();
      Object msg = ((Pair)data).cdr();
      Symbol modulename = (Symbol)ports.get(port);
      if(modulename==null) {
        System.out.println("No module for port "+port+
                           "; Active ports: "+ports.toString());
      } else {
        ProcessorModule pm = getModule(modulename);
        if(pm!=null && pm.signalEvent(port,msg)) 
          setChanged();
      }
    } else if(name == Simulator.kDelay) { // handle delay, ticks or transmit
      if(data instanceof Pair) { // pass delay termination to initiator
        Object signal = ((Pair)data).cdr();
        ProcessorModule pm = getModule((Symbol)((Pair)data).car());
        if(pm!=null && pm.signalEvent(Simulator.kDelay,signal)) 
          setChanged();
      } else if(data==kClock) { // distribute ticks to all clocks
        boolean dirty = false;
        time++;
        Integer t = new Integer(time);
        Iterator i = timers.iterator();
        while(i.hasNext()) {
          Symbol l = (Symbol)i.next();
          ProcessorModule pm = getModule(l);
          if(pm!=null) dirty |= pm.signalEvent(kClock,t);
        }
        timer(1,kClock);
        if(dirty) setChanged(); // signal change if it happened
      } else if(data==kTransmit) { // do regular transmission
        while(!sendQ.isEmpty()) { // if does singles; while does constant
          Pair message = (Pair)sendQ.pop();
          broadcast(message);
          // send the callback notification
          Symbol port = (Symbol)message.car();
          ProcessorModule pm = getModule((Symbol)ports.get(port));
          if(pm!=null && pm.signalEvent(kTransmit,message))
            setChanged();
        }
        timer(0.5+2*Math.random(),kTransmit);
      } else {
        // this shouldn't ever happen
        System.out.println("Odd data: "+data.toString());
      }
    }
    notifyObservers(); // flag changes for display, if they've happened
  }

  // module interface
  Hashtable modules = new Hashtable();
  public void addModule(ProcessorModule m) { 
    modules.put(m.getName(),m);
  }
  public ProcessorModule getModule(String name) {
    return getModule(Symbol.GetSymbol(name));
  }
  public ProcessorModule getModule(Symbol name) {
    return (ProcessorModule)modules.get(name);
  }

  // clock interface
  // sends events of type "tick" with value time
  Vector timers = new Vector();
  public void addClockListener(ProcessorModule m) {
    timers.add(m.getName());
  }
  public void timer(ProcessorModule m,double delay,Symbol signal) {
    timer(delay,new Pair(m.getName(),signal));
  }

  // port interface
  // communicates via events of type=port and data=message
  Hashtable ports = new Hashtable();
  utils.Queue sendQ = new utils.Queue();
  public void openNetworkPort(ProcessorModule m, Symbol port) {
    ports.put(port,m.getName());
  }
  // sends a "message-sent" callback when it's plucked off the Q
  public void sendMessage(Symbol port, Object message) {
    sendQ.push(new Pair(port,message));
  }
  public void colorme(Color c) { super.colorme(c); }
}

