package brn.swans.route.metric;

import java.util.List;

import jist.swans.Constants;
import jist.swans.net.NetAddress;

import brn.swans.route.metric.LinkStat.LinkInfo;
import brn.swans.route.metric.LinkTable.LinkData;

/**
 * The Estimated Transmission Count metric (ETX).
 *
 * @author Zubow
 */
public class ETXMetric implements LinkStat.Metric {

  public final static int INFINITE_METRIC = 9999;

  protected LinkTable linkTable;

  protected Integer dataRate;

  /**
   * @param dataRate the bit rate to calculate etx for.
   */
  public ETXMetric(Object id, LinkTable linkTable, int dataRate) {
//    this.id = id;
    this.linkTable = linkTable;
    this.dataRate = Integer.valueOf(dataRate);
  }

  public LinkTable getLinkTable() {
    return linkTable;
  }

  public static int etxMetric(List linkinfos, Integer dataRate) {
    if (linkinfos.size() <= 0)
      return INFINITE_METRIC;

    if (0 > dataRate.intValue()) {
      LinkInfo info = (LinkInfo) linkinfos.get(0);

      if (info != null) {
        int v = info.fwd * info.rev;
        if (v != 0)
          return 100 * 100 * 100 / v;
      }
    } else {
      int controlRate = Constants.BANDWIDTH_6Mbps;
      if (dataRate.intValue() == Constants.BANDWIDTH_1Mbps
          || dataRate.intValue() == Constants.BANDWIDTH_2Mbps
          || dataRate.intValue() == Constants.BANDWIDTH_5_5Mbps
          || dataRate.intValue() == Constants.BANDWIDTH_11Mbps)
        controlRate = Constants.BANDWIDTH_1Mbps;

      LinkInfo lnfAck = null;
      LinkInfo lnfData = null;
      for (int x = 0; x < linkinfos.size(); x++) {
        LinkInfo linkInfo = (LinkInfo) linkinfos.get(x);
        if (linkInfo.rate.intValue() == controlRate
            && (lnfAck == null || lnfAck.size > linkInfo.size)) {
          lnfAck = linkInfo;
        }
        if (linkInfo.rate.equals(dataRate)
            && (lnfData == null || lnfData.size > linkInfo.size)) {
          lnfData = linkInfo;
        }
      }

      if (lnfAck == null || lnfData == null)
        return INFINITE_METRIC;

      int v = lnfData.fwd * lnfAck.rev;
      if (v != 0)
        return 100 * 100 * 100 / v;
    }
    return INFINITE_METRIC;
  }

  public void updateLink(NetAddress from, NetAddress to, long seq, List linkinfo) {
    int metric = ETXMetric.etxMetric(linkinfo, dataRate);

    LinkData link = linkTable.getLink(from, to);
    link.update(seq, 0, metric, metric);
    
    link = linkTable.getLink(to, from);
    link.update(seq, 0, metric, metric);
  }
}
