package jist.swans.net;

import jist.swans.Node;
import jist.swans.mac.MacAddress;
import jist.swans.mac.MacInterface;
import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.misc.MessageEvent;
import jist.swans.route.RouteInterface;

public abstract class AbstractNet {

  // ////////////////////////////////////////////////
  // Events
  //

  // Incoming packet from/outgoing packet to higher layer
  public static class RecvFromTransEvent extends MessageEvent {
    public RecvFromTransEvent() { super(false); }
    public RecvFromTransEvent(int nodeId) { super(nodeId); }
  }

  public static class SendToTransEvent extends MessageEvent {
    public MacAddress lastHop;
    public byte macId;

    public SendToTransEvent() { super(false); }
    public SendToTransEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, MessageAnno anno, MacAddress lastHop, byte macId) {
      this.lastHop = lastHop;
      this.macId = macId;
      super.handle(msg, anno);
    }
  }

  // enqueueTraceEvent, dequeueTraceEvent: Queue events

  public static class EnqueueEvent extends MessageEvent {
    public NetAddress localAddr;
    public MacAddress nextHop;
    public MessageQueue queue;

    public EnqueueEvent() { super(false); }
    public EnqueueEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, MessageAnno anno, NetAddress localAddr,
        MacAddress nextHop,
        MessageQueue queue) {
      this.localAddr = localAddr;
      this.nextHop = nextHop;
      this.queue = queue;
      super.handle(msg, anno);
    }
  }

  public static class DequeueEvent extends MessageEvent {
    public NetAddress localAddr;
    public MacAddress nextHop;
    public MessageQueue queue;

    public DequeueEvent() { super(false); }
    public DequeueEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, MessageAnno anno, NetAddress localAddr,
        MacAddress nextHop,
        MessageQueue queue) {
      this.localAddr = localAddr;
      this.nextHop = nextHop;
      this.queue = queue;
      super.handle(msg, anno);
    }
  }

  /** packet that was given (sent) to the lower layer is feedbacked. */
  public static class SendToMacFinishEvent extends MessageEvent {
    public NetAddress localAddr;

    public SendToMacFinishEvent() { super(false); }
    public SendToMacFinishEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, MessageAnno anno, NetAddress localAddr) {
      this.localAddr = localAddr;
      super.handle(msg, anno);
    }
  }

  /** packet is given (sent) to the lower layer */
  public static class SendToMacEvent extends MessageEvent {
    public NetAddress localAddr;
    public MacAddress nextHop;

    public SendToMacEvent() { super(false); }
    public SendToMacEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, MessageAnno anno, NetAddress localAddr,
        MacAddress nextHop) {
      this.localAddr = localAddr;
      this.nextHop = nextHop;
      super.handle(msg, anno);
    }
  }

  /** packet is received from lower layer for this node */
  public static class RecvFromMacEvent extends MessageEvent {
    public NetAddress localAddr;
    public MacAddress lastHop;

    public RecvFromMacEvent() { super(false); }
    public RecvFromMacEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, MessageAnno anno, NetAddress localAddr,
        MacAddress lastHop) {
      this.localAddr = localAddr;
      this.lastHop = lastHop;
      super.handle(msg, anno);
    }
  }

  /** packet is dropped due to various reasons */
  public static class LinkDropEvent extends MessageEvent {
    public NetAddress localAddr;
    public MacAddress nextHop;
    /** whether we received (true) or sent (false) this packet */
    public boolean incoming;

    public LinkDropEvent() { super(false); }
    public LinkDropEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, MessageAnno anno, NetAddress localAddr,
        MacAddress nextHop,
        boolean incoming) {
      this.localAddr = localAddr;
      this.nextHop = nextHop;
      this.incoming = incoming;
      super.handle(msg, anno);
    }
  }

  // ////////////////////////////////////////////////
  // constants
  //

  // ////////////////////////////////////////////////
  // locals
  //

  protected Node node;

  protected RecvFromTransEvent recvFromTransEvent;

  protected SendToTransEvent sendToTransEvent;

  protected EnqueueEvent enqueueEvent;

  protected DequeueEvent dequeueEvent;

  protected SendToMacEvent sendToMacEvent;

  protected SendToMacFinishEvent sendToMacFinishEvent;

  protected RecvFromMacEvent recvFromMacEvent;

  protected LinkDropEvent linkDropEvent;


  // ////////////////////////////////////////////////
  // initialization
  //
  
  public AbstractNet() {
    recvFromTransEvent = new RecvFromTransEvent();
    sendToTransEvent = new SendToTransEvent();
    enqueueEvent = new EnqueueEvent();
    dequeueEvent = new DequeueEvent();
    sendToMacEvent = new SendToMacEvent();
    sendToMacFinishEvent = new SendToMacFinishEvent();
    recvFromMacEvent = new RecvFromMacEvent();
    linkDropEvent = new LinkDropEvent();
  }

  // ////////////////////////////////////////////////
  // accessors
  //

  public Node getNode() {
    return node;
  }

  public void setNode(Node node) {
    this.node = node;

    recvFromTransEvent = new RecvFromTransEvent(node.getNodeId());
    sendToTransEvent = new SendToTransEvent(node.getNodeId());
    enqueueEvent = new EnqueueEvent(node.getNodeId());
    dequeueEvent = new DequeueEvent(node.getNodeId());
    sendToMacEvent = new SendToMacEvent(node.getNodeId());
    sendToMacFinishEvent = new SendToMacFinishEvent(node.getNodeId());
    recvFromMacEvent = new RecvFromMacEvent(node.getNodeId());
    linkDropEvent = new LinkDropEvent(node.getNodeId());
  }

  // ////////////////////////////////////////////////
  // overwrites
  //

  public abstract NetAddress getAddress();

  public abstract NetInterface getProxy();

  public abstract byte addInterface(MacInterface proxy, MessageQueue queue);

  public abstract void setProtocolHandler(int protocol, NetInterface.NetHandler handler);

  public abstract void setRouting(RouteInterface routeProxy);

  // ////////////////////////////////////////////////
  // operations
  //

}
