/**
 *
 */
package brn.swans.route.metric;

import java.util.List;

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

import jist.swans.Constants;
import jist.swans.mac.MacAddress;
import jist.swans.net.NetAddress;
import jist.swans.radio.RadioInterface;

/**
 * Simple hop count metric without link probing
 *
 * @author kurth
 * TODO implement
 */
public class RouteHopCountMetric extends AbstractRouteMetric
  implements RouteMetricInterface {

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

  /** distincts between bad and no link */
  private static final int MAX_LINK_METRIC = 1000;

  /** metric of a perfect link */
  private static final int MIN_LINK_METRIC = 100;



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

  /**
   * McExOR logger.
   */
//  private static Logger log = Logger.getLogger(RouteHopCountMetric.class.getName());

  /**
   * The IP address of this node.
   */
//  private NetAddress localAddr;

  /**
   * Link table.
   */
  private LinkTable linkTable;

  /** link table querier */
  private LinkTableQuerier querier;

  private ArpTableInterface arpTable;


  // ////////////////////////////////////////////////
  // initialization
  //

  public RouteHopCountMetric(int nodeId, NetAddress localAddr,
      MacAddress localMacAddr, ArpTableInterface arpTable) {
//    this.localAddr = localAddr;
    this.linkTable = new LinkTableSlow();
    this.arpTable = arpTable;
    this.querier = new LinkTableQuerier(linkTable, 1 * Constants.SECOND);

    linkTable.setLinkMetricChangedEvent(
        new LinkMetricChangedEvent(this.linkTable, nodeId));

    if (linkMetricCreatedEvent.isActive())
      linkMetricCreatedEvent.handle(linkTable, nodeId);
  }


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

  public LinkTable getLinkTable() {
    return linkTable;
  }


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

  /* (non-Javadoc)
   * @see brn.swans.route.metric.hop.RouteMetricInterface#addEventListener(brn.swans.route.metric.hop.RouteMetricInterface.EventListener)
   */
  public void addEventListener(EventListener listener) {
    linkTable.addEventListener(listener);
  }

  /* (non-Javadoc)
   * @see brn.swans.route.metric.hop.RouteMetricInterface#getChannels()
   */
  public RadioInterface.RFChannel[] getChannels() {
    return new RadioInterface.RFChannel[] { RadioInterface.DEFAULT_RF_CHANNEL };
  }

  /* (non-Javadoc)
   * @see brn.swans.route.metric.hop.RouteMetricInterface#getHomeChannel(jist.swans.net.NetAddress)
   */
  public RadioInterface.RFChannel getHomeChannel(NetAddress nb) {
    return RadioInterface.RFChannel.DEFAULT_RF_CHANNEL;
  }

  public RadioInterface.RFChannel getHomeChannel() {
    return RadioInterface.RFChannel.DEFAULT_RF_CHANNEL;
  }

  /* (non-Javadoc)
   * @see brn.swans.route.metric.hop.RouteMetricInterface#getLinkMetric(jist.swans.net.NetAddress, jist.swans.net.NetAddress)
   */
  public int getLinkMetric(NetAddress addrSrc, NetAddress addrDst) throws NoLinkExistsException {
    return querier.getLinkMetric(addrSrc, addrDst);
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.RouteMetricInterface#getLink(jist.swans.net.NetAddress, jist.swans.net.NetAddress)
   */
  public LinkData getLink(NetAddress addrSrc, NetAddress addrDst) {
    return linkTable.getLinkInfo(addrSrc, addrDst);
  }

  /* (non-Javadoc)result
   * @see brn.swans.route.metric.hop.RouteMetricInterface#getNeighbors(jist.swans.net.NetAddress)
   */
  public List getNeighbors(NetAddress addr) {
    return querier.getNeighbors(addr);
  }

  /* (non-Javadoc)
   * @see brn.swans.route.metric.hop.RouteMetricInterface#getRouteMetric(java.util.List, int)
   */
  public int getRouteMetric(List route, int maxLinkMetric, int maxLinkEtx)
      throws NoRouteExistsException, NoLinkExistsException {
    return querier.getRouteMetric(route, maxLinkMetric, maxLinkEtx);
  }

  /* (non-Javadoc)
   * @see brn.swans.route.metric.hop.RouteMetricInterface#queryRoute(jist.swans.net.NetAddress, jist.swans.net.NetAddress)
   */
  public List queryRoute(NetAddress addrSrc, NetAddress addrDst,
      int maxLinkMetric, int maxLinkEtx) throws NoRouteExistsException {
    return querier.queryRoute(addrSrc, addrDst, maxLinkMetric, maxLinkEtx, true);
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.RouteMetricInterface#queryRoute(jist.swans.net.NetAddress, jist.swans.net.NetAddress, int, boolean)
   */
  public List queryRoute(NetAddress addrSrc, NetAddress addrDst,
      int maxLinkMetric, int maxLinkEtx, boolean fromCache) throws NoRouteExistsException {
    return querier.queryRoute(addrSrc, addrDst, maxLinkMetric, maxLinkEtx, fromCache);
  }

  /* (non-Javadoc)
   * @see brn.swans.route.metric.hop.RouteMetricInterface#updateBothLinks(java.lang.Object, java.lang.Object, int, boolean)
   */
  public boolean updateBothLinks(Object a, Object b, int metric, boolean permanent) {
    if (metric > MAX_LINK_METRIC) {
      linkTable.removeLink(a, b);
      linkTable.removeLink(b, a);
//      querier.invalidateRoute(a, b);
      return true;
    }
    // TODO why changing metric??
    metric = MIN_LINK_METRIC;

    // TODO set etx
    LinkData link = linkTable.getLink(a, b);
    link.update(0, 0, metric, -2);

    // TODO set etx
    link = linkTable.getLink(b, a);
    link.update(0, 0, metric, -2);

    return true;
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.ArpTableInterface#getArpTable()
   */
  public ArpTableInterface getArpTable() {
    return this.arpTable;
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.RouteMetricInterface#removeLink(jist.swans.net.NetAddress, jist.swans.net.NetAddress)
   */
  public void removeLink(NetAddress addr1, NetAddress addr2) {
    linkTable.removeLink(addr1, addr2);
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.RouteMetricInterface#getLinkMetricOld(jist.swans.net.NetAddress, jist.swans.net.NetAddress)
   */
  public int getLinkMetricOld(NetAddress prev, NetAddress curr) {
    try {
      return getLinkMetric(prev, curr);
    } catch (NoLinkExistsException e) {
      return 9999;
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.RouteMetricInterface#getRouteMetric(java.util.List, int)
   */
  public int getRouteMetric(List route, int invalidRouteMetric) {
    try {
      return getRouteMetric(route, invalidRouteMetric, invalidRouteMetric);
    } catch (NoRouteExistsException e) {
      return 9999;
    } catch (NoLinkExistsException e) {
      return 9999;
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.RouteMetricInterface#queryRoute(jist.swans.net.NetAddress, jist.swans.net.NetAddress, int)
   */
  public List queryRoute(NetAddress src, NetAddress dst, int csMinMetric) {
    try {
      return queryRoute(src, dst, csMinMetric, csMinMetric);
    } catch (NoRouteExistsException e) {
      return null;
    }
  }

}
