//////////////////////////////////////////////////
// JIST (Java In Simulation Time) Project
// Timestamp: <RadioInterface.java Tue 2004/04/13 18:22:52 barr glenlivet.cs.cornell.edu>
//

// Copyright (C) 2004 by Cornell University
// All rights reserved.
// Refer to LICENSE for terms and conditions of use.

package jist.swans.radio;

import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.Constants;

import jist.runtime.JistAPI;

/**
 * Defines the interface of all Radio entity implementations.
 *
 * @author Rimon Barr &lt;barr+jist@cs.cornell.edu&gt;
 * @version $Id: RadioInterface.java,v 1.16 2004/04/13 22:28:55 barr Exp $
 * @since SWANS1.0
 */

public interface RadioInterface
{

  //////////////////////////////////////////////////
  // RF channel
  //
  public static final RFChannel DEFAULT_RF_CHANNEL
          = new RFChannel(Constants.FREQUENCY_DEFAULT, Constants.CHANNEL_DEFAULT);

  //////////////////////////////////////////////////
  // communication (mac)
  //

  /**
   * Start transmitting message. Puts radio into transmission mode and contacts
   * other radios that receive the signal. Called from mac entity.
   *
   * @param msg message object to transmit
   * @param delay time to the wire
   * @param duration time on the wire
   * @deprecated use {@link #transmit(Message, MessageAnno, long, long, long)}
   */
  void transmit(Message msg, long delay, long duration, MessageAnno anno);

  /**
   * Start transmitting message. Puts radio into transmission mode and contacts
   * other radios that receive the signal. Called from mac entity.
   *
   * @param msg message object to transmit
   * @param anno message object to transmit
   * @param predelay time before tx start (e.g. rx/tx turnaround)
   * @param duration time on the wire
   * @param postdelay time after tx until tx finish (e.g. tx/rx turnaround)
   */
  void transmit(Message msg, MessageAnno anno, 
      long predelay, long duration, long postdelay);

  /**
   * End message transmission. Putting the radio back into idle (or possibly
   * receiving) mode. Called from mac entity.
   */
  void endTransmit();

  //////////////////////////////////////////////////
  // communication (field)
  //

  /**
   * Start receiving message. Puts radio into receive or sensing mode depending
   * on the message power and the state of the radio. A radio that is currently
   * transmitting will ignore incoming messages. Called from field entity.
   *
   * @param msg incoming message
   * @param power signal strength of incoming message (units: mW)
   * @param duration time until end of transmission (units: simtime)
   */
  void receive(Message msg, Double power, Long duration);

  /**
   * End message reception. Puts the radio back into sensing or idle mode, and
   * sends the received message to upper layers for processing, if no error has
   * occurred during the reception. Called from field entity.
   *
   * @param power signal strength of incoming message (units: mW)
   */
  void endReceive(Message msg, Double power, RFChannel rfChannel, Object event);


  //////////////////////////////////////////////////
  // sleep (application)
  //

  /**
   * Put radio you in sleep/awake mode.
   *
   * @param sleep sleep/awake switch
   */
  void setSleepMode(boolean sleep);


  //////////////////////////////////////////////////
  // channel (application)
  //

  /**
   * An RF channel is represented by this object:
   * - frequency band
   * - channel no.
   * 
   * note: objects are immutable
   */
  public class RFChannel implements Comparable, JistAPI.Timeless {

    public static final RFChannel INVALID_RF_CHANNEL
            = new RFChannel(Constants.INVALID_FREQUENCY, Constants.INVALID_CHANNEL);

    public static final RFChannel DEFAULT_RF_CHANNEL
            = new RFChannel(Constants.FREQUENCY_DEFAULT, Constants.CHANNEL_DEFAULT);

    /** the RF frequency band of this channel, e.g.: 2.4GHz or 5.2GHz */
    protected long frequency;
    /** the channel number used within this frequency band (channel numbering always starts with 1) */
    protected int channel;

    public RFChannel(long frequency, int channel) {
      this.frequency = frequency;
      this.channel = channel;
    }
    
    public RFChannel(RFChannel other) {
      this.frequency = other.frequency;
      this.channel = other.channel;
    }

    public long getFrequency() {
      return frequency;
    }

    public int getChannel() {
      return channel;
    }

    public String toString() {
      return frequency / Constants.FREQUENCY_UNIT + "Ghz(" + channel + ")";
    }

    public String toShortString() {
      return frequency + "/" + channel;
    }

    public int compareTo(Object o) {
      RFChannel other = (RFChannel)o;
      if (frequency != other.frequency) {
        return frequency < other.frequency ? -1 : 1;
      } else {
        return channel < other.channel ? -1 : 1;
      }
    }

    public int hashCode() {
      final int PRIME = 31;
      int result = 1;
      result = PRIME * result + channel;
      result = PRIME * result + (int) (frequency ^ (frequency >>> 32));
      return result;
    }

    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final RFChannel other = (RFChannel) obj;
      if (channel != other.channel)
        return false;
      if (frequency != other.frequency)
        return false;
      return true;
    }
  }

  /**
   * Sets the physical channel.
   *
   * @param channel the channel to set.
   * @param delay   the channel switch delay
   * note: only possible if not transmitting.
   */
  void setChannel(RFChannel channel, long delay);

  /**
   * Finally sets the channel. Called by the channel itself.
   *
   * @param channel the channel to set.
   */
  void endSetChannel(RFChannel channel);

  /**
   * returns the active channel.
   *
   * @return the active channel
   * @throws JistAPI.Continuation marks the method as blocking
   */
  RFChannel getChannel() throws JistAPI.Continuation;

  // Delegator
  public static final class Dlg implements RadioInterface, JistAPI.Proxiable {
    RadioInterface impl;

    public Dlg(RadioInterface impl) {
      this.impl = impl;
    }

    public RadioInterface getProxy() {
      return (RadioInterface) JistAPI.proxy(this, RadioInterface.class);
    }

    public void receive(Message msg, Double power, Long duration) {
      impl.receive(msg, power, duration);
    }

    public void endReceive(Message msg, Double power, RFChannel rfChannel, Object event) {
      impl.endReceive(msg, power, rfChannel, event);
    }

    public void transmit(Message msg, long delay, long duration, MessageAnno anno) {
      impl.transmit(msg, delay, duration, anno);
    }

    public void endTransmit() {
      impl.endTransmit();
    }

    public void setSleepMode(boolean sleep) {
      impl.setSleepMode(sleep);
    }

    public void setChannel(RFChannel channel, long delay) {
      impl.setChannel(channel, delay);
    }

    public void endSetChannel(RFChannel channel) {
      impl.endSetChannel(channel);
    }

    public RFChannel getChannel() throws JistAPI.Continuation {
      return impl.getChannel();
    }

    public void transmit(Message msg, MessageAnno anno, long predelay,
        long duration, long postdelay) {
      impl.transmit(msg, anno, predelay, duration, postdelay);
    }
  }


  public interface Noise {
    /**
     * Retrieves the current transmission signal strength or noise in mW.
     * Note:
     *  - the returned value strongly depends on the radio model used.
     *  - the value is always greater than the sensitivity threshold
     *
     * @return the current noise in mW if greater than sensitivity threshold or zero if below
     */
    double getNoise_mW() throws JistAPI.Continuation;

    // Delegator
    public static final class Dlg implements Noise, JistAPI.Proxiable {
      private Noise impl;
      public Dlg(Noise impl) {
        super();
        this.impl = impl;
      }
      public double getNoise_mW() throws JistAPI.Continuation {
        return impl.getNoise_mW();
      }
    }
  }

} // interface: RadioInterface

