//////////////////////////////////////////////////
// JIST (Java In Simulation Time) Project
// Timestamp: <MacDumb.java Sun 2005/03/13 11:06:58 barr rimbase.rimonbarr.com>
//

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

package jist.swans.mac;

import jist.swans.radio.RadioInterface;
import jist.swans.radio.RadioInfo;
import jist.swans.net.NetInterface;
import jist.swans.misc.MessageAnno;
import jist.swans.misc.Message;
import jist.swans.Constants;
import jist.swans.phy.Phy802_11;

import jist.runtime.JistAPI;

/**
 * A dumb, pass-through mac implementation.
 *
 * @author Rimon Barr &lt;barr+jist@cs.cornell.edu&gt;
 * @version $Id: MacDumb.java,v 1.17 2005/03/13 16:11:55 barr Exp $
 * @since SWANS1.0
 */

public class MacDumb extends AbstractMac implements MacInterface
{

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

  // entities
  /** radio entity. */
  private RadioInterface radioEntity;
  /** network entity. */
  private NetInterface netEntity;
  /** network interface identifier. */
  private byte netId;
  /** self-referencing proxy entity. */
  private final MacInterface self;

  // state
  /** radio mode: transmit, receive, etc. */
  private byte radioMode;
  /** local mac address. */
  private MacAddress localAddr;
  /** link bandwidth. */
  private final int bandwidth;
  /** whether in promiscuous mode. */
  private boolean promisc;
  /** message factory */
  protected MacDumbMessageFactory msgFactory;

  //////////////////////////////////////////////////
  // initialize
  //

  /**
   * Create a new "dumb" mac entity. Does not perform any collision
   * avoidance or detection. Simply does not transmit (drops) packet
   * if it is current receiving.
   *
   * @param addr local mac address
   * @param radioInfo radio information
   */
  public MacDumb(MacAddress addr, RadioInfo radioInfo)
  {
    this.localAddr = addr;
    bandwidth = radioInfo.getBitratesSupported()[0];// Bitrate();
    radioMode = Constants.RADIO_MODE_IDLE;
    promisc = Constants.MAC_PROMISCUOUS_DEFAULT;
    self = (MacInterface)JistAPI.proxy(new MacInterface.Dlg(this), MacInterface.class);
  }

  //////////////////////////////////////////////////
  // accessors
  //

  /**
   * Set promiscuous mode (whether to pass all packets through).
   *
   * @param promisc promiscuous flag
   */
  public void setPromiscuous(boolean promisc)
  {
    this.promisc = promisc;
  }

  //////////////////////////////////////////////////
  // entity hookup
  //

  /**
   * Hook up with the network entity.
   *
   * @param net network entity
   * @param netid network interface number
   */
  public void setNetEntity(NetInterface net, byte netid)
  {
    if(!JistAPI.isEntity(net)) throw new IllegalArgumentException("expected entity");
    this.netEntity = net;
    this.netId = netid;
  }

  /**
   * Hook up with the radio entity.
   *
   * @param radio radio entity
   */
  public void setRadioEntity(RadioInterface radio)
  {
    if(!JistAPI.isEntity(radio)) throw new IllegalArgumentException("expected entity");
    this.radioEntity = radio;
  }

  public MacDumbMessageFactory getMsgFactory() {
    return this.msgFactory;
  }

  public void setMsgFactory(MacDumbMessageFactory msgFactory) {
    this.msgFactory = msgFactory;
  }

  /**
   * Return self-referencing proxy entity.
   *
   * @return proxy entity
   */
  public MacInterface getProxy()
  {
    return self;
  }

  public Phy802_11 getPhy() {
    return null;
  }

  public MacInfo getMacInfo() {
    return null;
  }

  public MacInterface getMacProxy() {
    return self;
  }

  /*
   * (non-Javadoc)
   * @see java.lang.Object#toString()
   */
  public String toString() {
    return localAddr.getId() + "-" + localAddr.toString();
  }

  //////////////////////////////////////////////////
  // MacInterface methods
  //

  /** {@inheritDoc} */
  public void setRadioMode(byte mode)
  {
    this.radioMode = mode;
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacInterface#peek(jist.swans.misc.Message, jist.swans.misc.MessageAnno)
   */
  public void peek(Message msg, MessageAnno anno)
  {
    // pass ...
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacInterface#receive(jist.swans.misc.Message, jist.swans.misc.MessageAnno)
   */
  public void receive(Message msg, MessageAnno anno)
  {
    MacDumbMessage mdm = (MacDumbMessage)msg;
    JistAPI.sleep(Constants.LINK_DELAY);

    if (receiveEvent.isActive())
      receiveEvent.handle(msg, anno);

    if(netEntity == null)
      return;

    if(MacAddress.ANY.equals(mdm.getDst())
      || localAddr.equals(mdm.getDst())) {
      netEntity.receive(msgFactory.getPayload(mdm), mdm.getSrc(), netId, false, anno);
    }
    else if(promisc) {
      netEntity.receive(msgFactory.getPayload(mdm), mdm.getSrc(), netId, true, anno);
    }
  }

  /**
   * Compute packet transmission time at current bandwidth.
   *
   * @param msg packet to transmit
   * @return time to transmit given packet at current bandwidth
   */
  private long transmitTime(Message msg)
  {
    int size = msg.getSize();
    if(size==Constants.ZERO_WIRE_SIZE)
    {
      return Constants.EPSILON_DELAY;
    }
    return size * Constants.SECOND/bandwidth;
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacInterface#send(jist.swans.misc.Message, jist.swans.mac.MacAddress, jist.swans.misc.MessageAnno)
   */
  public void send(Message msg, MacAddress nextHop, MessageAnno anno)
  {
    JistAPI.sleep(Constants.LINK_DELAY);
    if(radioMode==Constants.RADIO_MODE_IDLE)
    {
      MacDumbMessage mdm = msgFactory.createData(this.localAddr, nextHop, msg); 
      final long transmitTime = transmitTime(mdm);

      if (sendEvent.isActive())
        sendEvent.handle(mdm, anno);

      radioEntity.transmit(mdm, 0, transmitTime, anno);
      JistAPI.sleep(transmitTime+Constants.EPSILON_DELAY);
    }
    if(netEntity!=null)
      netEntity.endSend(msg, netId, anno);
  }

  public MacAddress getAddress() {
    return this.localAddr;
  }
}
