package brn.analysis.mcexor;

import org.apache.log4j.Logger;
import jist.swans.misc.Event;
import jist.swans.misc.Location;
import jist.swans.mac.AbstractMac;
import jist.swans.Node;
import jist.swans.net.NetMessage;
import jist.swans.radio.RadioNoise;

import java.util.*;

import brn.swans.mac.MacMcExORMessage;
import brn.swans.mac.MacMcExOR;

public class McExOREval extends EvalHandler {

  /** logger for mac events. */
  public static final Logger log = Logger.getLogger(McExOREval.class.getName());

  public final static int SEND_DUPL_RATIO = 2;

  private List nodes;
  private int serverNode;

  //
  // SEND
  //
  private Collection sendData = new Vector();
  private Collection sendAck = new Vector();
  private Collection sendRts = new Vector();
  private Collection sendCts = new Vector();

  //
  // RECV
  //
  private Collection recvData = new Vector();
  private Collection recvAck  = new Vector();
  private Collection recvRts  = new Vector();
  private Collection recvCts  = new Vector();

  //
  // RECV FOREIGN
  //
  private Collection recvForeignData  = new Vector();
  private Collection recvForeignAck   = new Vector();
  private Collection recvForeignRts   = new Vector();
  private Collection recvForeignCts   = new Vector();

  //
  // DUPS, RETRIES, FWD
  //
  private Collection duplPackets    = new Vector();
  private Collection retryPackets   = new Vector();
  private Collection fwdPackets     = new Vector();
  private Collection fwdDuplPackets = new Vector();

  //
  // DISTANCE GAIN
  //
  private Collection distances = new Vector();

  // --------------------------------------------------------------------------
  //
  public McExOREval(List nodes, int serverNode) {
    super();
    this.nodes = nodes;
    this.serverNode = serverNode;
  }


  public void registerHandlers() {

    // --------------------------------------------------------------------------
    // SendEvent
    // --------------------------------------------------------------------------
    Event.addHandler(AbstractMac.SendEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.SendEvent ev = (AbstractMac.SendEvent) event;

        if (ev.getData() instanceof MacMcExORMessage.Data) {
          MacMcExORMessage.Data msg = (MacMcExORMessage.Data)ev.getData();

          if (msg.getDsts().isBroadCast())
            return;

          sendData.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Ack) {
          MacMcExORMessage.Ack msg = (MacMcExORMessage.Ack)ev.getData();
          sendAck.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Rts) {
          MacMcExORMessage.Rts msg = (MacMcExORMessage.Rts)ev.getData();
          sendRts.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Cts) {
          MacMcExORMessage.Cts msg = (MacMcExORMessage.Cts)ev.getData();
          sendCts.add(new Integer(msg.getId()));
        }
      }
    });

    // --------------------------------------------------------------------------
    // ReceiveEvent
    // --------------------------------------------------------------------------
    Event.addHandler(AbstractMac.ReceiveEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.ReceiveEvent ev = (AbstractMac.ReceiveEvent) event;

        if (ev.getData() instanceof MacMcExORMessage.Data) {
          MacMcExORMessage.Data msg = (MacMcExORMessage.Data)ev.getData();
          if (msg.getDsts().isBroadCast())
            return;
          recvData.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Ack) {
          MacMcExORMessage.Ack msg = (MacMcExORMessage.Ack)ev.getData();
          recvAck.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Rts) {
          MacMcExORMessage.Rts msg = (MacMcExORMessage.Rts)ev.getData();
          recvRts.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Cts) {
          MacMcExORMessage.Cts msg = (MacMcExORMessage.Cts)ev.getData();
          recvCts.add(new Integer(msg.getId()));
        }
      }
    });

    // --------------------------------------------------------------------------
    // ReceiveForeignEvent
    // --------------------------------------------------------------------------
    Event.addHandler(AbstractMac.ReceiveForeignEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.ReceiveForeignEvent ev = (AbstractMac.ReceiveForeignEvent) event;

        if (ev.getData() instanceof MacMcExORMessage.Data) {
          MacMcExORMessage.Data msg = (MacMcExORMessage.Data)ev.getData();
          if (msg.getDsts().isBroadCast())
            return;
          recvForeignData.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Ack) {
          MacMcExORMessage.Ack msg = (MacMcExORMessage.Ack)ev.getData();
          recvForeignAck.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Rts) {
          MacMcExORMessage.Rts msg = (MacMcExORMessage.Rts)ev.getData();
          recvForeignRts.add(new Integer(msg.getId()));
        } else if (ev.getData() instanceof MacMcExORMessage.Cts) {
          MacMcExORMessage.Cts msg = (MacMcExORMessage.Cts)ev.getData();
          recvForeignCts.add(new Integer(msg.getId()));
        }
      }
    });

    // --------------------------------------------------------------------------
    // ForwarderEvent
    // --------------------------------------------------------------------------
    Event.addHandler(AbstractMac.ForwarderEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.ForwarderEvent ev = (AbstractMac.ForwarderEvent) event;

        MacMcExORMessage.Data msg = (MacMcExORMessage.Data)ev.getData();

        Integer id = new Integer(msg.getId());
        if (fwdPackets.contains(id)) {
          fwdDuplPackets.add(id);
          return;
        } else {
          fwdPackets.add(id);
        }

        // gain is the difference between A (distance between src node and server node)
        // and B (distance between dst node and server node)

        long srcId = msg.getSrc().getId();

        Node srcNode = getNode((int)srcId);
        Node dstNode = getNode(ev.nodeId);
        Node srvNode = getNode(serverNode);

        int srcRadioId = ((RadioNoise)srcNode.getRadio(0)).getRadioInfo().getId();
        int dstRadioId = ((RadioNoise)dstNode.getRadio(0)).getRadioInfo().getId();
        int srvRadioId = ((RadioNoise)srvNode.getRadio(0)).getRadioInfo().getId();

        Location.Location2D srcLoc = (Location.Location2D)srcNode.getField(0)
            .getRadioData(new Integer(srcRadioId)).getLoc();
        Location.Location2D dstLoc = (Location.Location2D)dstNode.getField(0)
            .getRadioData(new Integer(dstRadioId)).getLoc();
        Location.Location2D srvLoc = (Location.Location2D)srvNode.getField(0)
            .getRadioData(new Integer(srvRadioId)).getLoc();

        float dist_before = srcLoc.distance(srvLoc);
        float dist_now = dstLoc.distance(srvLoc);

        float gain = dist_before - dist_now;

        //log.warn("from " + srcId + " to " + ev.nodeId + " distance gain of " + distance);

        distances.add(new Long(Math.round(gain)));
      }
    });

    // --------------------------------------------------------------------------
    // DuplicateEvent
    // --------------------------------------------------------------------------
    Event.addHandler(AbstractMac.DuplicateEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.DuplicateEvent ev = (AbstractMac.DuplicateEvent) event;

        MacMcExORMessage.Data msg = (MacMcExORMessage.Data)ev.getData();
        duplPackets.add(new Integer(msg.getId()));
      }
    });

    // --------------------------------------------------------------------------
    // RetryEvent
    // --------------------------------------------------------------------------
    Event.addHandler(AbstractMac.RetryEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.RetryEvent ev = (AbstractMac.RetryEvent) event;

        NetMessage.Ip msg = (NetMessage.Ip)ev.getData();
        retryPackets.add(new Integer(msg.getId()));
      }
    });
  }

  public Collection getDetectedDuplicates() {
    return duplPackets;
  }

  public Collection getNotDetectedDuplicates() {
    return fwdDuplPackets;
  }

  public Collection getSendData() {
    return sendData;
  }

  public Collection getRetryPackets() {
    return retryPackets;
  }

  public Collection getRecvData() {
    return recvData;
  }

  public Collection getSendAck() {
    return sendAck;
  }

  public Collection getRecvForeignAck() {
    return recvForeignAck;
  }

  public Collection getRecvAck() {
    return recvAck;
  }

  public Collection getDistances() {
    return distances;
  }

  public Collection getSendRts() {
    return sendRts;
  }

  public Collection getSendCts() {
    return sendCts;
  }

  public Collection getRecvRts() {
    return recvRts;
  }

  public Collection getRecvCts() {
    return recvCts;
  }

  public Collection getRecvForeignData() {
    return recvForeignData;
  }

  public Collection getRecvForeignRts() {
    return recvForeignRts;
  }

  public Collection getRecvForeignCts() {
    return recvForeignCts;
  }

  public Collection getFwdPackets() {
    return fwdPackets;
  }

  public long getCumulatedDistances() {
    Iterator it = distances.iterator();

    long sum = 0;
    while(it.hasNext()) {
      Long d = (Long)it.next();
      sum += d.longValue();
    }
    return sum;
  }

  private Node getNode(int id) {
    Iterator it = nodes.iterator();
    while (it.hasNext()) {
      Node node = (Node)it.next();

      if (node.getNodeId() == id)
        return node;
    }
    return null;
  }
}
