package brn.swans.field;

import jist.swans.Constants;
import jist.swans.Node;
import jist.swans.mac.AbstractMac;
import jist.swans.route.AbstractRoute;
import jist.swans.radio.AbstractRadio;
import jist.swans.radio.RadioNoise;
import jist.swans.radio.RadioInterface;

import java.util.List;

import brn.swans.route.NGRouteMcExOR;
import brn.swans.route.RouteDsrBrnInterface;
import brn.swans.route.RouteDsrDiscoveryProactive;
import brn.swans.route.metric.LinkStat;
import brn.swans.route.metric.RouteEtxEttMetric;
import brn.swans.route.metric.RouteMetricInterface;
import brn.swans.mac.MacMcExOR;

/**
 * This class assigns RF channels (e.g. channel 1) to nodes accordingly to different algorithms.
 * Note: RF frequencies (2.4GHz or 5.2GHz) are not assigned here !!!
 */
public class ChannelPattern {

  private final static int RADIO_IDX  = 0;
  private final static int MAC_IDX    = 0;
  /**
   * Sets a channel on the specified entity.
   *
   * @param node    The node to apply the channel to.
   * @param channel The channel to apply.
   */
  public static void applyChannel(Node node, int channel) {

    RadioInterface.RFChannel rfCh
        = new RadioInterface.RFChannel(RadioInterface.RFChannel.DEFAULT_RF_CHANNEL.getFrequency(), channel);

    if (node.getRadios().size() < RADIO_IDX+1)
      return;

    AbstractRadio radio = node.getRadio(RADIO_IDX);

    if (radio instanceof RadioNoise) {
      RadioNoise rnoise = ((RadioNoise)radio);

      rnoise.getRadioInfo().setChannel(channel);
      RadioInterface.RFChannel newRfChannel
              = new RadioInterface.RFChannel(rnoise.getChannel().getFrequency(), channel);
      rnoise.setCurrentState(newRfChannel);
    }

    AbstractRoute route = node.getRoute();
    if (route instanceof NGRouteMcExOR) {
      RouteMetricInterface rmetric = ((NGRouteMcExOR) route).getRouteMetric();
      if (rmetric instanceof RouteEtxEttMetric) {
        LinkStat linkStat = ((RouteEtxEttMetric) rmetric).getLinkStat();
        linkStat.setHomeChannel(rfCh);
      }
      RouteDsrBrnInterface.Discovery discovery = ((NGRouteMcExOR) route).getDiscovery();

      if (discovery instanceof RouteDsrDiscoveryProactive) {
        ((RouteDsrDiscoveryProactive) discovery).setHomeChannel(channel);
      }
    }

    AbstractMac mac = node.getMac(MAC_IDX);
    if (mac instanceof MacMcExOR) {
      ((MacMcExOR)mac).setHomeChannel(rfCh);
    }

    /*
    node.getOptions().channel = channel;

    MacMcExOR mac = (MacMcExOR)node.getMac();
    mac.setHomeChannel(node.getOptions().channel);

    RouteMcExOR route = (RouteMcExOR) node.getRoute();
    RouteMcExORFlood flood = (RouteMcExORFlood) route.getFlooding();
    LinkStat linkStat = (LinkStat) route.getLinkStat();
    if (flood != null)
      flood.setHomeChannel(node.getOptions().channel);
    linkStat.setHomeChannel(node.getOptions().channel);
    */
  }

  /**
   * Sets the channel in and round robin way.
   *
   * @author kurth
   */
  public static class RoundRobin {

    public static void assign(List listNodes, int channels) {
      for (int i = 0; i < listNodes.size(); i++)
        applyChannel((Node) listNodes.get(i), (i % channels) + 1);
    }
  }

//  /**
//   * Sets the channel in an diagonal way.
//   *
//   * @author kurth
//   */
//  public static class Diag {
//
//    public static void assign(List listNodes, int channels, Location field, Location density) {
//      /*
//      int xNodes = Szenario.getXNodes(field, density);
//      int yNodes = Szenario.getYNodes(field, density);
//
//      for (int i = 0; i < xNodes; i++)
//        for (int j = 0; j < yNodes; j++)
//          applyChannel((Node) listNodes.get(j + i * yNodes), (i + j) % channels);
//      */
//    }
//  }


  /**
   * Sets a single channel to all nodes
   */
  public static class Single {

    public static void assign(List listNodes, int channel) {
      for (int i = 0; i < listNodes.size(); i++)
        applyChannel((Node) listNodes.get(i), channel);
    }
  }

  /**
   * Sets a random channel to the nodes.
   *
   * @author kurth
   */
  public static class Random {

    public static void assign(List listNodes, int channels) {
      for (int i = 0; i < listNodes.size(); i++)
        applyChannel((Node) listNodes.get(i), Constants.random.nextInt(channels));
    }
  }

//  /**
//   * Sets a channel to the nodes via offset pattern.
//   *
//   * @author kurth
//   */
//  public static class Offset {
//
//    public static void assign(List listNodes, int channels, Location field, Location density) {
//      /*
//      int xNodes = Szenario.getXNodes(field, density);
//      int yNodes = Szenario.getYNodes(field, density);
//
//      int ch = -1;
//      if (channels % 2 == 0) {
//        for (int i = 0; i < xNodes; i++) {
//          for (int j = 0; j < yNodes; j++) {
//            ch = (i + j) % channels;
//            applyChannel((Node) listNodes.get(j + i * yNodes), ch);
//          }
//        }
//      } else {
//        for (int i = 0; i < xNodes; i++) {
//          ch = (i * 2) % channels;
//          for (int j = 0; j < yNodes; j++) {
//            applyChannel((Node) listNodes.get(j + i * yNodes), ch);
//            ch = (ch + 1) % channels;
//          }
//        }
//      }
//      */
//    }
//  }
//
//  /**
//   * Special channel pattern for string configurations.
//   *
//   * @author kurth
//   */
//  public static class String {
//
//    public static void assign(List listNodes, int channels, int xClouds, int yClouds, int bobbles) {
//      // Starting nodes
//      int currNumber = 0;
//      for (int j = 0; j < yClouds; j++) {
//        applyChannel((Node) listNodes.get(currNumber), j % channels);
//        currNumber++;
//      }
//
//      // create bobble nodes
//      for (int i = 1; i < xClouds - 1; i++) {
//        for (int j = 0; j < yClouds; j++) {
//          for (int k = 0; k < bobbles; k++) {
//            applyChannel((Node) listNodes.get(currNumber), j % channels);
//            currNumber++;
//          }
//        }
//      }
//
//      // create ending col
//      for (int j = yClouds - 1; j >= 0; j--) {
//        applyChannel((Node) listNodes.get(currNumber), j % channels);
//        currNumber++;
//      }
//    }
//  }
//
//  /**
//   * Colors the graph vertically.
//   *
//   * @author Kurth
//   */
//  public static class Vertical {
//    public static void assign(List listNodes, int channels, int xClouds, int yClouds, int bobbles) {
//      // Starting nodes
//      int currNumber = 0;
//      for (int j = 0; j < yClouds; j++) {
//        applyChannel((Node) listNodes.get(currNumber), 0);
//        currNumber++;
//      }
//
//      // create bobble nodes
//      for (int i = 1; i < xClouds - 1; i++) {
//        for (int j = 0; j < yClouds; j++) {
//          for (int k = 0; k < bobbles; k++) {
//            applyChannel((Node) listNodes.get(currNumber), (i - 1) % channels);
//            currNumber++;
//          }
//        }
//      }
//
//      // create ending col
//      for (int j = yClouds - 1; j >= 0; j--) {
//        applyChannel((Node) listNodes.get(currNumber), (xClouds - 2 + channels) % channels);
//        currNumber++;
//      }
//    }
//  }

}

