package brn.swans.route.metric;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import jist.runtime.Main;
import jist.runtime.Util;
import jist.swans.net.NetAddress;

/**
 * Fast link table implementation based on direct access.
 * NOTE: keys must be net addresses. the net address ids must be reasonable small.
 *
 * @author kurth
 */
public class LinkTableFast extends LinkTable {

  public static final int MAX_NUMBER_ENTRIES = 5000;

  protected HostData[] hosts;

  protected LinkData[][] links;

  public LinkTableFast() {
    this.hosts = new HostData[26];
    this.links = new LinkData[26][];
  }

  public LinkTableFast(long tau) {
    super(tau);
    this.hosts = new HostData[26];
    this.links = new LinkData[26][];
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.LinkTable#getHostInfo(java.lang.Object)
   */
  public HostData getHostInfo(Object from) {
    try {
      return hosts[((NetAddress)from).getId()];
    }
    catch (ClassCastException e) {
      throw new Error("Please use NetAddresses with this LinkTable");
    }
    catch (ArrayIndexOutOfBoundsException e) {
      int maxId = ((NetAddress)from).getId();
      if (maxId > MAX_NUMBER_ENTRIES || maxId < 0)
        throw new Error("Overflow or underflow, whatever...");
      HostData[] newHosts = new HostData[maxId + 23];
      System.arraycopy(hosts, 0, newHosts, 0, hosts.length);
      this.hosts = newHosts;
      return null;
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.LinkTable#setHostInfo(java.lang.Object, brn.swans.route.metric.LinkTable.HostInfo)
   */
  protected void setHostInfo(Object from, HostData nfrom) {
    try {
      hosts[((NetAddress)from).getId()] = nfrom;
    }
    catch (ClassCastException e) {
      throw new Error("Please use NetAddresses with this LinkTable");
    }
    catch (ArrayIndexOutOfBoundsException e) {
      int maxId = ((NetAddress)from).getId();
      if (maxId > MAX_NUMBER_ENTRIES)
        throw new Error("Overflow");
      HostData[] newHosts = new HostData[maxId + 23];
      System.arraycopy(hosts, 0, newHosts, 0, hosts.length);
      hosts = newHosts;
      hosts[maxId] = nfrom;
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.LinkTable#getHosts()
   */
  public Collection getHosts() {
    List ret = new ArrayList();
    for (int i = 0; i < hosts.length; i++) {
      if (hosts[i] != null)
        ret.add(hosts[i]);
    }
    return ret;
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.LinkTable#getLinkInfo(java.lang.Object, java.lang.Object)
   */
  public LinkData getLinkInfo(Object from, Object to) {
    try {
      return links[((NetAddress)from).getId()][((NetAddress)to).getId()];
    }
    catch (ClassCastException e) {
      throw new Error("Please use NetAddresses with this LinkTable");
    }
    catch(NullPointerException e) {
      int fromId = ((NetAddress)from).getId();
      int toId = ((NetAddress)to).getId();
      if (Main.ASSERT) Util.assertion(null == links[fromId]);
      links[fromId] = new LinkData[toId + 23];
      return null;
    }
    catch (ArrayIndexOutOfBoundsException e) {
      int fromId = ((NetAddress)from).getId();
      int toId = ((NetAddress)to).getId();
      if (fromId > MAX_NUMBER_ENTRIES || toId > MAX_NUMBER_ENTRIES)
        throw new Error("Overflow");

      if (fromId >= links.length) {
        LinkData[][] newLinks = new LinkData[fromId + 23][];
        System.arraycopy(links, 0, newLinks, 0, links.length);
        links = newLinks;
      }
      if (null == links[fromId] || toId >= links[fromId].length) {
        LinkData[] newLinks = new LinkData[toId + 23];
        if (null != links[fromId])
          System.arraycopy(links[fromId], 0, newLinks, 0, links[fromId].length);
        links[fromId] = newLinks;
      }
      return null;
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.LinkTable#setLinkInfo(java.lang.Object, java.lang.Object, brn.swans.route.metric.LinkTable.LinkInfo)
   */
  protected void setLinkInfo(Object from, Object to, LinkData lnfo) {
    try {
      links[((NetAddress)from).getId()][((NetAddress)to).getId()] = lnfo;
    }
    catch (ClassCastException e) {
      throw new Error("Please use NetAddresses with this LinkTable");
    }
    catch(NullPointerException e) {
      int fromId = ((NetAddress)from).getId();
      int toId = ((NetAddress)to).getId();
      if (Main.ASSERT) Util.assertion(null == links[fromId]);
      links[fromId] = new LinkData[toId + 23];
      links[fromId][toId] = lnfo;
    }
    catch (ArrayIndexOutOfBoundsException e) {
      int fromId = ((NetAddress)from).getId();
      int toId = ((NetAddress)to).getId();
      if (fromId > MAX_NUMBER_ENTRIES || toId > MAX_NUMBER_ENTRIES)
        throw new Error("Overflow");

      if (fromId >= links.length) {
        LinkData[][] newLinks = new LinkData[fromId + 23][];
        System.arraycopy(links, 0, newLinks, 0, links.length);
        links = newLinks;
      }
      if (null == links[fromId] || toId >= links[fromId].length) {
        LinkData[] newLinks = new LinkData[toId + 23];
        if (null != links[fromId])
          System.arraycopy(links[fromId], 0, newLinks, 0, links[fromId].length);
        links[fromId] = newLinks;
      }
      links[fromId][toId] = lnfo;
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.swans.route.metric.LinkTable#getLinks()
   */
  public Collection getLinks() {
    List ret = new ArrayList();
    for (int i = 0; i < links.length; i++) {
      for (int j = 0; null != links[i] && j < links[i].length; j++) {
        if (links[i][j] != null)
          ret.add(links[i][j]);
      }
    }
    return ret;

  }

}
