package jist.swans.radio;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;

import jist.swans.Constants;
import jist.swans.misc.Util;

/**
 * RadioFactory creates RadioInfo objects for different MAC types. 
 * 
 * @author Oliver
 */
public class RadioFactory
{
  /**
   * Checks for a valid MAC type constant.
   *
   * @param macType
   */
  public static void checkMacType(short macType) {
    switch (macType) {
      case Constants.MAC_802_11:
      case Constants.MAC_802_11a:
      case Constants.MAC_802_11b:
      case Constants.MAC_802_11b_LONG:
      case Constants.MAC_802_11bg:
      case Constants.MAC_802_11g_PURE:
        break;

      default:
        throw new RuntimeException("Unknown MAC constant in createRadioInfo: "
            + macType);
    }
  }

  /**
   * @deprecated Replaced by
   *             {@link RadioFactory#createRadioInfo(short, InputStream)}.
   * 
   * @param inStream
   *          The property file for the radio instance to create as a stream.
   * @return A RadioInfo object that contains the properties from the given
   *         property file.
   */
  public static RadioInfo createRadioInfo(InputStream inStream) {

    Properties props = new Properties();
    String str = null;

    Hashtable frequencies           = new Hashtable();
    Hashtable frequencySpacings     = new Hashtable();
    Hashtable frequencyWidths       = new Hashtable();
    Hashtable numberOfChannels      = new Hashtable();
    Hashtable bitrateTxPower        = new Hashtable();
    Hashtable bitrateSnrThresholds  = new Hashtable();

    try {
      props.load(inStream);

      String macTypeStr = props.getProperty("mac").trim();
      short macType = 0;
      if(macTypeStr.equalsIgnoreCase("802.11a"))
        macType = Constants.MAC_802_11a;
      else if(macTypeStr.equalsIgnoreCase("802.11b"))
        macType = Constants.MAC_802_11b;
      else if(macTypeStr.equalsIgnoreCase("802.11g")
          || macTypeStr.equalsIgnoreCase("802.11bg"))
        macType = Constants.MAC_802_11g_PURE;
      else if(macTypeStr.equalsIgnoreCase("802.11ag")
          || macTypeStr.equalsIgnoreCase("802.11abg"))
        macType = Constants.MAC_802_11a | Constants.MAC_802_11g_PURE;
      else 
        throw new RuntimeException("unknown mac: " + macTypeStr);

      // Extract the values from the strings
      String[] bitrates = props.getProperty("bitrates").trim().split("\\s*,\\s*");
      String[] thresholds = props.getProperty("thresholds").trim().split("\\s*,\\s*");
      String[] powers = props.getProperty("txpowers").trim().split("\\s*,\\s*");

      if(bitrates.length != thresholds.length)
        throw new RuntimeException("# of bitrate and threshold values do not match!");
      if(bitrates.length != powers.length)
        throw new RuntimeException("# of bitrate and powers values do not match!");
      if(bitrates.length <= 0)
        throw new RuntimeException("No bitrates and thresholds specified!");

      // Fill the bitrate-threshold mapping with values.
      int /*bitrate = 0,*/ tempBitrate;
      for(int i=0; i<bitrates.length; i++) {

        tempBitrate = (int)(Double.parseDouble(bitrates[i])*Constants.BANDWIDTH_1Mbps);

        int idx = Constants.BITRATES_ALL.length - 1;
        while(Constants.BITRATES_ALL[idx] != tempBitrate) {
          idx--;
        }

        // Only allow predefined bitrates
        if( idx >= 0 ) {
            bitrateSnrThresholds.put(new Integer(tempBitrate),
                new Double( Double.parseDouble(thresholds[i]) * Constants.THRESHOLD_UNIT )
            );
            bitrateTxPower.put(new Integer(tempBitrate),
                new Double( Double.parseDouble(powers[i])));
        } else {
          throw new RuntimeException("Unknown bitrate: " + bitrates[i] + " Mbit/s");
        }
      } // for()

      String[] basic = props.getProperty("basicRates").trim().split("\\s*,\\s*");
      int[] basicRateSet = new int[basic.length];
      for (int i = 0; i < basic.length; i++) {
        basicRateSet[i] = (int)(Double.parseDouble(basic[i])*Constants.BANDWIDTH_1Mbps);
      }

      // Fill frequencies collection; allow arbitrary values
      String freqString = props.getProperty("frequencies");
      String[] freqs = freqString.trim().split("\\s*,\\s*");

      if(freqs.length <= 0) {
        throw new RuntimeException("No frequencies specified!");
      }

      for(int i=0; i<freqs.length; i++) {
        long tempFreq = Long.parseLong(freqs[i]) * Constants.FREQUENCY_UNIT;
        frequencies.put(new Short(macType), new Long(tempFreq));
      }

      // Fill frequency spacings; allow arbitrary values
      String freqSpacingString = props.getProperty("frequency_spacings");
      String[] freqSpacings = freqSpacingString.trim().split("\\s*,\\s*");
      double /*frequencySpacing = 0,*/ tempFreqSpacing;

      if(freqSpacings.length <= 0) {
        throw new RuntimeException("No frequency spacings specified!");
      }

      for(int i=0; i<freqSpacings.length; i++) {
        tempFreqSpacing = Double.parseDouble(freqSpacings[i]) * Constants.FREQUENCY_UNIT;
        frequencySpacings.put(new Short(macType), new Double(tempFreqSpacing));
      }

      // Fill frequency widths; allow arbitrary values
      String freqwidthString = props.getProperty("frequency_widths");
      String[] freqwidths = freqwidthString.trim().split("\\s*,\\s*");
      double /*frequencywidth = 0, */ tempFreqwidth;

      if(freqwidths.length <= 0) {
        throw new RuntimeException("No frequency widths specified!");
      }

      for(int i=0; i<freqwidths.length; i++) {
        tempFreqwidth = Double.parseDouble(freqwidths[i]) * Constants.FREQUENCY_UNIT;
        frequencyWidths.put(new Short(macType), new Double(tempFreqwidth));
      }

      // Fill numberofchannels
      String numChannelsString = props.getProperty("number_of_channels");
      String[] numChannels = numChannelsString.trim().split("\\s*,\\s*");

      if(numChannels.length <= 0) {
        throw new RuntimeException("No number of channels specified!");
      }

      for(int i=0; i<numChannels.length; i++) {
        tempFreqwidth = Integer.parseInt(numChannels[i]);
        numberOfChannels.put(new Short(macType), new Integer(Integer.parseInt(numChannels[i])));
      }

      // Name
      String name = (str = props.getProperty("name")) != null ?
          str : "WifiCard_" + freqs[0];

      // Transmission power
      double transmitPower = (str = props.getProperty("power")) != null ?
          Double.parseDouble(str) : Constants.TRANSMIT_DEFAULT;

      // Gain
      double gain = (str = props.getProperty("gain")) != null ?
          Double.parseDouble(str) : Constants.GAIN_DEFAULT;

      double sensitivity_dB = (str = props.getProperty("sensitivity")) != null ?
          Double.parseDouble(str) : Constants.SENSITIVITY_DEFAULT;

      // Create the new RadioInfo object
      RadioInfo radioInfo = new RadioInfo(
        name,
        frequencies,
        frequencySpacings,
        frequencyWidths,
        numberOfChannels,
        bitrateTxPower,
        bitrateSnrThresholds,
        basicRateSet,
        transmitPower,
        gain,
        sensitivity_dB,
        Constants.TEMPERATURE_DEFAULT,
        Constants.AMBIENT_NOISE_DEFAULT,
        macType
      );

      //TODO fetch this info from the file !!!!!!!!!!!
      radioInfo.setChannel(Constants.CHANNEL_DEFAULT);

      return radioInfo;

    } catch (IOException e) {
      throw new RuntimeException();
//      return createRadioInfoDefault80211b();
    }

  } // createRadioInfo()

  private static void keepInHashtable(Hashtable ht, Object[] keys) {
    try {
      List l = Arrays.asList(keys);
      Enumeration en = ht.keys();
      while (en.hasMoreElements()) {
        Object o = en.nextElement();
        if (!l.contains(o))
          ht.remove(o);
      }
    } catch (NumberFormatException nfe) {
    }
  }   
  
  private static void setAllToOneValue(Hashtable ht, Double power) {
    for (Object o : ht.keySet())
      ht.put(o, power);
    return;
  }
  
  /**
   * Creates a RadioInfo object from a base MAC type and an option stream.
   * Parses the stream into a Properties object and then calls
   * {@link RadioFactory#createRadioInfo(short, Properties)}. 
   * 
   * @param macBaseType
   *          The MAC type to use as a base for the new object (overridden by
   *          property "mac" in @param inStream)
   * @param inStream
   *          The property file for the radio instance to create. All options
   *          are optional, but certain combinations generate an Exception. See
   *          the following overview.
   * @return A RadioInfo object derived from a radio info object of the given
   *         MAC type, overridden by the properties contained in the given
   *         properties stream.
   * 
   * <pre>
   * mac=802.11g          // base MAC type for the new object
   * bitrates=5.5,6,11    // bitrates this radio should support
   * basicRates=6         // subset of bitrates
   * txpowers=21,19.5,19  // one (for all) or as many as bitrates
   * thresholds=10,13,17  // one (for all) or as many as bitrates
   * frequencies=2400     // one value only supported (for now)
   * frequency_spacings=5 // one value only supported (for now)
   * frequency_widths=20  // one value only supported (for now)
   * number_of_channels=1 // one value only supported (for now)
   * default_channel=1    // one value only supported (for now)
   * temperature=295      // env. temp in Kelvin
   * ambient_noise=0.0    // env. noise in mW</pre>
   */
  public static RadioInfo createRadioInfo(short macBaseType,
      InputStream inStream) {
    Properties props = new Properties();
    try {
      props.load(inStream);
      String macTypeStr = props.getProperty("mac");
      short macType = macBaseType;
      if (macTypeStr != null && (macTypeStr = macTypeStr.trim()).length() > 0) {
        if (macTypeStr.equalsIgnoreCase("802.11a"))
          macType = Constants.MAC_802_11a;
        else if (macTypeStr.equalsIgnoreCase("802.11b"))
          macType = Constants.MAC_802_11b;
        else if (macTypeStr.equalsIgnoreCase("802.11bg"))
          macType = Constants.MAC_802_11bg;
        else if (macTypeStr.equalsIgnoreCase("802.11g"))
          macType = Constants.MAC_802_11g_PURE;
        else
          throw new RuntimeException("Unknown MAC: " + macTypeStr + "! You "
              + "must use a known MAC type as the base for your custom MAC!");
      }
      return createRadioInfo(macType, props);
    } catch (IOException e) {
      throw new RuntimeException("Error parsing properties list!", e);
    }
  } 
  
  /**
   * Creates a RadioInfo object from a base MAC type and a Properties object.
   * 
   * @param macType
   *          The MAC type to use as a base for the new object.
   * @param props
   *          The Properties object for the radio instance to create. All
   *          options are optional, but certain combinations generate an
   *          Exception. For details about the options see
   *          {@link RadioFactory#createRadioInfo(short, InputStream)}.
   * @return A RadioInfo object derived from a radio info object of the given
   *         MAC type, overridden by the given properties.
   * @see RadioFactory#createRadioInfo(short, InputStream)
   */
  public static RadioInfo createRadioInfo(short macBaseType, Properties props) {
    String str = null;
    checkMacType(macBaseType);
    RadioInfo ri = createRadioInfoDefault(macBaseType);
    Short macTemplShort = new Short(macBaseType);
    // Keep the MAC type: used in MacInfo to set up 802.11a/b/g objects
    short macType = macBaseType; // Constants.MAC_CUSTOM;
    // TODO: If I wanna use only ONE object as key for all hashtables, TEST IT!
    Short macTypeShort = new Short(macType);

    // These have to be adapted before the c'tor for new object is called.
    Hashtable bitrateTxPower = ri.getBitrateTxPower();
    Hashtable bitrateSnrThresholds = ri.getBitrateSnrThresholds();
    int[] basicRateSet = ri.getBasicRateSet();
    
    // Change the MAC type key to the new MAC type in these HTs
    Hashtable freqsHt = new Hashtable();
    freqsHt.put(new Short(macType), ri.getSupportedFrequencies().get(
        macTemplShort));
    Hashtable chSpacingsHt = new Hashtable();
    chSpacingsHt.put(new Short(macType), ri.getSupportedChannelSpacings().get(
        macTemplShort));
    Hashtable chWidthsHt = new Hashtable();
    chWidthsHt.put(new Short(macType), ri.getSupportedChannelWidths().get(
        macTemplShort));
    Hashtable chCountHt = new Hashtable();
    chCountHt.put(new Short(macType), ri.getSupportedNumberOfChannels().get(
        macTemplShort));

    // Extract bitrate related values from the string
    Integer[] bitrates = Util.parseDoubleArrayToInt(props
        .getProperty("bitrates"), ",", true, Constants.BANDWIDTH_1Mbps);
    Double[] thresholds = Util.parseDoubleArray(
        props.getProperty("thresholds"), ",", true);
    Double[] powers = Util.parseDoubleArray(props.getProperty("txpowers"), ",",
        true);

    if (bitrates.length > 0) {
      boolean updateThr = false;
      boolean updatePow = false;
      if (bitrates.length > ri.getBitratesSupported().length
          || thresholds.length > 1 || powers.length > 1) {
        //
        // If you provide more bitrates than in the given radio info,
        // we need the same info for thresholds and txpower levels; or
        // if you provide more than one value for either threshold or
        // power, we need of course the same number of bitrates.
        //
        if (thresholds.length > 1)
          if (thresholds.length != bitrates.length)
            throw new RuntimeException("Number of bitrates (" + bitrates.length
                + ") and thresholds (" + thresholds.length + ") must match!");
          else
            updateThr = true;

        if (powers.length > 1)
          if (powers.length != bitrates.length)
            throw new RuntimeException("Number of bitrates (" + bitrates.length
                + ") and txpowers (" + powers.length + ") must match!");
          else
            updatePow = true;
      }
      keepInHashtable(bitrateSnrThresholds, bitrates);
      keepInHashtable(bitrateTxPower, bitrates);

      // Fill the bitrate-threshold/bitrate-txpower mappings with values.
      if (updateThr || updatePow) {
        for (int i = 0; i < bitrates.length; i++) {
          int tempBitrate = (int) bitrates[i].doubleValue();
          // Only allow predefined bitrates
          int idx = Util.isInArray(Constants.BITRATES_ALL, tempBitrate);
          if (idx >= 0) {
            if (updateThr)
              bitrateSnrThresholds.put(new Integer(tempBitrate), thresholds[i]
                  * Constants.THRESHOLD_UNIT);
            if (updatePow)
              bitrateTxPower.put(new Integer(tempBitrate), powers[i]);
          } else {
            throw new RuntimeException("Unsupported bitrate: " + bitrates[i]
                + " Mbit/s");
          }
        }
      } // if (updateThr || updatePow)
    }
    if (thresholds.length == 1)
      setAllToOneValue(bitrateSnrThresholds, thresholds[0]);
    else if (thresholds.length > 1 && bitrates.length == 0)
      throw new RuntimeException(
          "Cannot map threshold values to bitrates without provided bitrates!");
    if (powers.length == 1)
      setAllToOneValue(bitrateTxPower, powers[0]);
    else if (powers.length > 1 && bitrates.length == 0)
      throw new RuntimeException(
          "Cannot map tx power values to bitrates without provided bitrates!");
    // end bitrates

    // Basic rates
    Double[] basic = Util.parseDoubleArray(props.getProperty("basicRates"),
        ",", true, Constants.BANDWIDTH_1Mbps);
    if (basic.length > 0) {
      basicRateSet = new int[basic.length];
      for (int i = 0; i < basic.length; i++)
        basicRateSet[i] = (int) basic[i].doubleValue();
    }
    for (int i = 0; i < basicRateSet.length; i++)
      if (!bitrateSnrThresholds.keySet().contains(new Integer(basicRateSet[i])))
          throw new RuntimeException("Bitrates must contain all basic rates! ("
              + basicRateSet[i] + " missing)");

    // Fill frequencies collection; allow arbitrary values
    String freqString = props.getProperty("frequencies");
    if (freqString != null && freqString.length() > 0) {
      String[] freqs = freqString.trim().split("\\s*,\\s*");
      for (int i = 0; i < freqs.length; i++) {
        long tempFreq = Long.parseLong(freqs[i]) * Constants.FREQUENCY_UNIT;
        freqsHt.put(new Short(macType), new Long(tempFreq));
      }
    }

    // Fill channel spacing; allow arbitrary value
    String chSpacingStr = props.getProperty("channel_spacings");
    // deprecated option name: frequency_spacings
    if (chSpacingStr == null) chSpacingStr = props.getProperty("frequency_spacings");
    if (chSpacingStr != null && chSpacingStr.length() > 0) {
      String[] chSpacingsA = chSpacingStr.trim().split("\\s*,\\s*");
      if (chSpacingsA.length > 1)
        throw new RuntimeException("Only ONE channel spacing value allowed!");
      if (chSpacingsA.length > 0)
        chSpacingsHt.put(new Short(macType), new Double(Double
            .parseDouble(chSpacingsA[0])
            * Constants.FREQUENCY_UNIT));
    }

    // Fill channel width; allow arbitrary value
    String chWidthStr = props.getProperty("channel_widths");
 // deprecated option name: frequency_widths
    if (chWidthStr == null) chWidthStr = props.getProperty("frequency_widths");
    if (chWidthStr != null && chWidthStr.length() > 0) {
      String[] chWidth = chWidthStr.trim().split("\\s*,\\s*");
      if (chWidth.length > 1)
        throw new RuntimeException("Only ONE channel width value allowed!");
      if (chWidth.length > 0)
        chWidthsHt.put(new Short(macType), new Double(Double
            .parseDouble(chWidth[0])
            * Constants.FREQUENCY_UNIT));
    }

    // Fill numberofchannels
    String chCountStr = props.getProperty("number_of_channels");
    if (chCountStr != null && chCountStr.length() > 0) {
      String[] chCount = chCountStr.trim().split("\\s*,\\s*");
      if (chCount.length > 1)
        throw new RuntimeException("Only ONE channel count value allowed!");
      if (chCount.length > 0)
        chCountHt.put(new Short(macType), new Integer(Integer
            .parseInt(chCount[0])));
    }

    // Default channel
    int chDefault = (str = props.getProperty("default_channel")) != null
        && str.length() > 0 ? Integer.parseInt(str) : Constants.CHANNEL_DEFAULT;
    if (chDefault > ((Integer) chCountHt.get(macTypeShort)).intValue())
      throw new RuntimeException("Default channel must be <= channel count!");

    // Energy detection sensitivity
    double sensitivity_dB = (str = props.getProperty("sensitivity")) != null
        && str.length() > 0 ? Double.parseDouble(str)
        : Constants.SENSITIVITY_DEFAULT;

    // Temperature
    double temperature = (str = props.getProperty("temperature")) != null
        && str.length() > 0 ? Double.parseDouble(str)
        : Constants.TEMPERATURE_DEFAULT;

    // Ambient noise
    double ambientNoise_mW = (str = props.getProperty("ambient_noise")) != null
        && str.length() > 0 ? Double.parseDouble(str)
        : Constants.AMBIENT_NOISE_DEFAULT;

    // Name
    String name = (str = props.getProperty("name")) != null && str.length() > 0 ? str
        : "Wifi_Custom_" + freqsHt.get(macTypeShort);

    // Create the new RadioInfo object
    RadioInfo radioInfo = new RadioInfo(ri.getStatic(), ri.getDynamic(), 
        name, freqsHt, chSpacingsHt,
        chWidthsHt, chCountHt, bitrateTxPower, bitrateSnrThresholds,
        basicRateSet, sensitivity_dB, temperature, ambientNoise_mW, macType);
    // set default channel
    radioInfo.setChannel(chDefault);
    return radioInfo;
  } // createRadioInfo(short, InputStream)

  /**
   * Create a RadioInfo object with the radio properties that underlie the given
   * MAC layer type (a,b,g or combinations).
   * 
   * Using the values of the SPARKLAN Wmia 123ag card.
   * 
   * @param macType
   * @return RadioInfo object with default values for a wi-fi card of the given
   *         type.
   */
  public static RadioInfo createRadioInfoDefault(short macType) {

    checkMacType(macType);
    
    // Set supported frequencies
    Hashtable frequencies = new Hashtable();
    switch (macType) {
      case Constants.MAC_802_11a:
        frequencies.put(new Short(macType), new Long(Constants.FREQUENCY_5GHZ));
        break;
      case Constants.MAC_802_11:
      case Constants.MAC_802_11b:
      case Constants.MAC_802_11b_LONG:
      case Constants.MAC_802_11bg:
      case Constants.MAC_802_11g_PURE:
        frequencies.put(new Short(macType), new Long(Constants.FREQUENCY_2_4GHZ));
        break;
    }

    // Set channel properties
    Hashtable channelSpacings = new Hashtable();
    Hashtable channelWidths = new Hashtable();
    switch (macType) {
      case Constants.MAC_802_11a:
        channelSpacings.put(new Short(macType), new Double(Constants.CHANNEL_SPACING_80211a));
        channelWidths.put(new Short(macType), new Double(Constants.CHANNEL_WIDTH_80211a));
        break;
      case Constants.MAC_802_11:
      case Constants.MAC_802_11b:
      case Constants.MAC_802_11b_LONG:
        channelSpacings.put(new Short(macType), new Double(Constants.CHANNEL_SPACING_80211b));
        channelWidths.put(new Short(macType), new Double(Constants.CHANNEL_WIDTH_80211b));
        break;
      case Constants.MAC_802_11bg:
      case Constants.MAC_802_11g_PURE:
        channelSpacings.put(new Short(macType), new Double(Constants.CHANNEL_SPACING_80211g));
        channelWidths.put(new Short(macType), new Double(Constants.CHANNEL_WIDTH_80211g));
        break;
    }

    Hashtable numberOfChannels = new Hashtable();
    switch (macType) {
      case Constants.MAC_802_11a:
        numberOfChannels.put(new Short(macType), new Integer(Constants.ORTOGONONAL_CHANNEL_NUMBER_80211a));
        break;

      case Constants.MAC_802_11:
      case Constants.MAC_802_11b:
      case Constants.MAC_802_11b_LONG:
        numberOfChannels.put(new Short(macType), new Integer(Constants.ORTOGONONAL_CHANNEL_NUMBER_80211b));
        break;

      case Constants.MAC_802_11bg:
      case Constants.MAC_802_11g_PURE:
        numberOfChannels.put(new Short(macType), new Integer(Constants.ORTOGONONAL_CHANNEL_NUMBER_80211g));
        break;
    }

    // Set bit-rates and reception threshold power values
    Hashtable bitrateSnrThresholds = new Hashtable();
    switch (macType) {

      case Constants.MAC_802_11:
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_1Mbps),
            new Double(7 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_2Mbps),
            new Double(8 * Constants.THRESHOLD_UNIT));
        break;

      case Constants.MAC_802_11b:
      case Constants.MAC_802_11b_LONG:
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_1Mbps),
            new Double(7 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_2Mbps),
            new Double(8 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_5_5Mbps),
            new Double(9 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_11Mbps),
            new Double(13 * Constants.THRESHOLD_UNIT));
        break;

      case Constants.MAC_802_11bg:
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_1Mbps),
            new Double(7 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_2Mbps),
            new Double(8 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_5_5Mbps),
            new Double(9 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_11Mbps),
            new Double(13 * Constants.THRESHOLD_UNIT));
        // keep going

      case Constants.MAC_802_11g_PURE:
      case Constants.MAC_802_11a:
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_6Mbps),
            new Double(10 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_9Mbps),
            new Double(11 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_12Mbps),
            new Double(12 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_18Mbps),
            new Double(13 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_24Mbps),
            new Double(17 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_36Mbps),
            new Double(20 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_48Mbps),
            new Double(26 * Constants.THRESHOLD_UNIT));
        bitrateSnrThresholds.put(new Integer(Constants.BANDWIDTH_54Mbps),
            new Double(30 * Constants.THRESHOLD_UNIT));
        break;
    }

    // Transmission power values for individual bitrates
    Hashtable bitrateTxPower = new Hashtable();
    switch (macType) {

      case Constants.MAC_802_11:
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_1Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_2Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        break;

      case Constants.MAC_802_11b:
      case Constants.MAC_802_11b_LONG:
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_1Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_2Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_5_5Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_11Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        break;

      case Constants.MAC_802_11bg:
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_1Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_2Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_5_5Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_11Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        // keep going

      case Constants.MAC_802_11g_PURE:
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_6Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_9Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_12Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_18Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_24Mbps),
            new Double(19 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_36Mbps),
            new Double(18 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_48Mbps),
            new Double(16 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_54Mbps),
            new Double(15 * Constants.THRESHOLD_UNIT));
        break;

      case Constants.MAC_802_11a:
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_6Mbps),
            new Double(17 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_9Mbps),
            new Double(17 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_12Mbps),
            new Double(17 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_18Mbps),
            new Double(17 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_24Mbps),
            new Double(17 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_36Mbps),
            new Double(16 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_48Mbps),
            new Double(14 * Constants.THRESHOLD_UNIT));
        bitrateTxPower.put(new Integer(Constants.BANDWIDTH_54Mbps),
            new Double(12 * Constants.THRESHOLD_UNIT));
        break;
    }

    // Set the basic rates
    int[] basicRates = null;
    switch (macType) {
      case Constants.MAC_802_11:
        basicRates = Constants.BASIC_RATES_80211;
        break;

      case Constants.MAC_802_11b:
      case Constants.MAC_802_11b_LONG:
        basicRates = Constants.BASIC_RATES_80211B;
        break;

      case Constants.MAC_802_11bg:
        basicRates = Constants.BASIC_RATES_80211BG;
        break;

      case Constants.MAC_802_11a:
      case Constants.MAC_802_11g_PURE:
        basicRates = Constants.BASIC_RATES_80211AG;
        break;
    }

    double sensitivity_dB = Constants.SENSITIVITY_DEFAULT;

    // Create the radio info
    RadioInfo radioInfo = new RadioInfo(
        "Sparklan Card Wmia123ag",             // name
        frequencies,                           // frequencies
        channelSpacings,                       // spacing between channel centers
        channelWidths,                         // channel widths
        numberOfChannels,                      // number of channels
        bitrateTxPower,                        // modulations
        bitrateSnrThresholds,                  // bitrate-threshold mapping
        basicRates,                            // minimal basic rate set
        Constants.TRANSMIT_DEFAULT,            // default transmit power
        Constants.GAIN_DEFAULT,                // default antenna gain
        sensitivity_dB,                        // default signal recpt. threshold
        Constants.TEMPERATURE_DEFAULT,         // default environment temperature
        Constants.AMBIENT_NOISE_DEFAULT,       // default environment noise
        macType                                // default mac type
    );

    // Set initial values
    radioInfo.setChannel(Constants.CHANNEL_DEFAULT);

    return radioInfo;
  } // createRadioInfoDefault

  /** @return RadioInfo object with default values (802.11a). */
  public static RadioInfo createRadioInfoDefault80211a() {
    return createRadioInfoDefault(Constants.MAC_802_11a);
  } // createRadioInfoDefault80211a

  /** @return RadioInfo object with default values (802.11b). */
  public static RadioInfo createRadioInfoDefault80211b() {
    return createRadioInfoDefault(Constants.MAC_802_11b);
  } // createRadioInfoDefault80211b

  /** @return RadioInfo object with default values (802.11bg). */
  public static RadioInfo createRadioInfoDefault80211g() {
    return createRadioInfoDefault(Constants.MAC_802_11bg);
  } // createRadioInfoDefault80211g

  /** @return RadioInfo object with default values (802.11g pure). */
 public static RadioInfo createRadioInfoDefault80211gPure() {
   return createRadioInfoDefault(Constants.MAC_802_11g_PURE);
 } // createRadioInfoDefault80211g

  public static RadioInfo createRadioInfoFastEthernet() {
    short macType = Constants.MAC_DUMB;
    int bandwidth = 100 * Constants.BANDWIDTH_1Mbps;
    
    // Set supported frequencies
    Hashtable frequencies = new Hashtable();
    frequencies.put(new Short(macType), new Long(Constants.FREQUENCY_2_4GHZ));

    // Set channel properties
    Hashtable channelSpacings = new Hashtable();
    Hashtable channelWidths = new Hashtable();
    channelSpacings.put(new Short(macType), new Double(Constants.CHANNEL_SPACING_80211g));
    channelWidths.put(new Short(macType), new Double(Constants.CHANNEL_WIDTH_80211g));

    Hashtable numberOfChannels = new Hashtable();
    numberOfChannels.put(new Short(macType), new Integer(1));

    // Set bit-rates and reception threshold power values
    Hashtable bitrateSnrThresholds = new Hashtable();
    bitrateSnrThresholds.put(new Integer(bandwidth),
        new Double(7 * Constants.THRESHOLD_UNIT));

    // Transmission power values for individual bitrates
    Hashtable bitrateTxPower = new Hashtable();
    bitrateTxPower.put(new Integer(bandwidth),
        new Double(19 * Constants.THRESHOLD_UNIT));

    // Set the basic rates
    int[] basicRates = new int[] { bandwidth };

    // Create the radio info
    RadioInfo radioInfo = new RadioInfo(
        "Fast Ethernet",             // name
        frequencies,                           // frequencies
        channelSpacings,                       // spacing between channel centers
        channelWidths,                         // channel widths
        numberOfChannels,                      // number of channels
        bitrateTxPower,                        // modulations
        bitrateSnrThresholds,                  // bitrate-threshold mapping
        basicRates,                            // minimal basic rate set
        Constants.TRANSMIT_DEFAULT,            // default transmit power
        Constants.GAIN_DEFAULT,                // default antenna gain
        Constants.SENSITIVITY_DEFAULT,                        // default signal recpt. threshold
        Constants.TEMPERATURE_DEFAULT,         // default environment temperature
        Constants.AMBIENT_NOISE_DEFAULT,       // default environment noise
        macType                                // default mac type
    );

    // Set initial values
    radioInfo.setChannel(Constants.CHANNEL_DEFAULT);

    return radioInfo;
  } // createRadioInfoFastEthernet

  public static RadioInfo createRadioInfoDefault802154() {
   short macType = Constants.MAC_802_11b;
   int bandwidth = Constants.BANDWIDTH_40Kbps /* BANDWIDTH bps */;
   
   // Set supported frequencies
   Hashtable frequencies = new Hashtable();
   frequencies.put(new Short(macType), new Long(Constants.FREQUENCY_2_4GHZ));

   // Set channel properties
   Hashtable channelSpacings = new Hashtable();
   Hashtable channelWidths = new Hashtable();
   channelSpacings.put(new Short(macType), new Double(Constants.CHANNEL_SPACING_80211g));
   channelWidths.put(new Short(macType), new Double(Constants.CHANNEL_WIDTH_80211g));

   Hashtable numberOfChannels = new Hashtable();
   numberOfChannels.put(new Short(macType), new Integer(1));

   // Set bit-rates and reception threshold power values
   double temperature = Constants.TEMPERATURE_DEFAULT;
   double ambientNoise_mW = Constants.AMBIENT_NOISE_DEFAULT;
   double thermalNoise_mW = Constants.BOLTZMANN * temperature * 1000.0
     * Constants.CHANNEL_WIDTH_80211g;
   double background_mW = ambientNoise_mW + thermalNoise_mW;
   Hashtable bitrateSnrThresholds = new Hashtable();
   bitrateSnrThresholds.put(new Integer(bandwidth),
       new Double(Constants.THRESHOLD_DEFAULT - Util.toDB(background_mW)));

   // Transmission power values for individual bitrates
   Hashtable bitrateTxPower = new Hashtable();
   bitrateTxPower.put(new Integer(bandwidth),
       new Double(Constants.TRANSMIT_DEFAULT));

   // Set the basic rates
   int[] basicRates = new int[] { bandwidth };

  // Create the radio info
   RadioInfo radioInfo = new RadioInfo(
       "IEEE 802.15.4",                       // name
       frequencies,                           // frequencies
       channelSpacings,                       // spacing between channel centers
       channelWidths,                         // channel widths
       numberOfChannels,                      // number of channels
       bitrateTxPower,                        // modulations
       bitrateSnrThresholds,                  // bitrate-threshold mapping
       basicRates,                            // minimal basic rate set
       Constants.TRANSMIT_DEFAULT,            // default transmit power
       Constants.GAIN_DEFAULT,                // default antenna gain
       Constants.SENSITIVITY_DEFAULT,         // default signal recpt. threshold
       temperature ,                          // default environment temperature
       ambientNoise_mW,                       // default environment noise
       macType                                // default mac type
   );

   // Set initial values
   radioInfo.setChannel(Constants.CHANNEL_DEFAULT);

   return radioInfo;

  }
}
