//////////////////////////////////////////////////
// JIST (Java In Simulation Time) Project
// Timestamp: <Mac802_11.java Thu 2005/02/10 11:45:06 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.runtime.JistAPI;
import jist.runtime.Main;
import jist.swans.Constants;
import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.misc.Util;
import jist.swans.phy.Phy802_11;
import jist.swans.phy.PhyMessage;
import jist.swans.radio.RadioInfo;
import jist.swans.rate.RateControlAlgorithmIF;

import org.apache.log4j.Logger;

/**
 * Implementation of IEEE 802_11a/b/g with: - RTS/CTS
 *
 * zubow: make it more modular.
 *
 * @author Rimon Barr &lt;barr+jist@cs.cornell.edu&gt;
 * @author Zubow
 * @version $Id: Mac802_11.java,v 1.62 2005/02/10 16:52:28 barr Exp $
 * @since SWANS1.0
 */
public class Mac802_11 extends MacDcf {

	/** MAC logger. */
	public static final Logger log = Logger
			.getLogger(jist.swans.mac.Mac802_11.class);

	/**
	 * Threshold packet size for fragmentation. Default=2346. Broadcast packets
	 * are not fragmented.
	 */
	public int THRESHOLD_FRAGMENT = 2346;

	/**
	 * TODO control rate?
	 */
	// public long avgTransmitTimeUnicastPacket(int length,
	// int dataRate, int controlRate, int shortRetries, int longRetries) {
	// return macInfo.avgTransmitTimeUnicastPacket(this.phy, length, dataRate,
	// controlRate, shouldRTS(), shortRetries, longRetries, this.msgFactory);
	// }
	// mac modes
	// /** mac mode: waiting for virtual carrier sense to RTS. */
	// public static final byte MAC_MODE_SNAV_RTS = 4;
	/** mac mode: waiting for CTS packet. */
	public static final byte MAC_MODE_SWFCTS = 5;

	/** mac mode: waiting for DATA packet. */
	public static final byte MAC_MODE_SWFDATA = 6;

	/** mac mode: transmitting RTS packet. */
	public static final byte MAC_MODE_XRTS = 8;

	/** mac mode: transmitting CTS packet. */
	public static final byte MAC_MODE_XCTS = 9;

	public String getModeString(byte mode) {
		switch (mode) {
			// case MAC_MODE_SNAV_RTS:
			// return "NAV_RTS";
			case MAC_MODE_SWFCTS:
				return "WF_CTS";
			case MAC_MODE_SWFDATA:
				return "WF_DATA";
			case MAC_MODE_XRTS:
				return "X_RTS";
			case MAC_MODE_XCTS:
				return "X_CTS";
			default:
				return super.getModeString(mode);
		}
	}

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

	// retry counts
	// use "long retry counter" for frames larger than RTS_THRESHOLD

	/**
	 * The long (packet) retry counter is the transmission counter for packets
	 * that are longer than the RTS threshold of this station. For the RTS and
	 * CTS packets increment the short retry counter in case of tx failure.
	 */
	protected byte longRetry;

	/**
	 * an RTS packet, if one arrived, or null the packet is stored in
	 * {@link #receiveRts} and released in {@link #receiveData}, (or
	 * earlier/later).
	 */
	protected Mac802_11Message.Rts rtsRcvd;

	/** corresponding anno */
	protected MessageAnno rtsRcvdAnno;

	/** factory for mac messages */
	private MacMessageFactory.M802_11 msgFactory;

	// ////////////////////////////////////////////////
	// initialization
	//

	/**
   * @deprecated Instantiate new 802_11b entity.
   * @param addr
   *          local mac address
   * @param radioInfo
   *          radio properties
   */
  public Mac802_11(MacAddress addr, RadioInfo radioInfo) {
    this(addr, radioInfo, MacInfo.create(radioInfo.getMacType(),
        radioInfo.getBitratesSupported(), radioInfo.getBasicRateSet()));
  }

  /** @deprecated */
  public Mac802_11(MacAddress addr, RadioInfo radioInfo,
      RateControlAlgorithmIF rateSelection) {
    this(addr, radioInfo, MacInfo.create(radioInfo.getMacType(),
        radioInfo.getBitratesSupported(), radioInfo.getBasicRateSet()),
        rateSelection, new Phy802_11(radioInfo));
  }

  public Mac802_11(MacAddress addr, RadioInfo radioInfo, MacInfo macInfo) {
    this(addr, radioInfo, macInfo, null, new Phy802_11(radioInfo));
  }

  public Mac802_11(MacAddress addr, RadioInfo radioInfo, MacInfo macInfo,
      RateControlAlgorithmIF rateSelection) {
    this(addr, radioInfo, macInfo, rateSelection, new Phy802_11(radioInfo));
  }

	public Mac802_11(MacAddress addr, RadioInfo radioInfo, MacInfo macInfo,
			RateControlAlgorithmIF rateSelection, Phy802_11 phy) {
		super(addr, radioInfo, macInfo, rateSelection, phy);
		// retry counts
		longRetry = 0;
		// received packet short term memory
		rtsRcvd = null;
		rtsRcvdAnno = null;
	}

	public void setMsgFactory(MacMessageFactory.Dcf msgFactory) {
		super.setMsgFactory(msgFactory);
		this.msgFactory = (MacMessageFactory.M802_11) msgFactory;
	}

	// ////////////////////////////////////////////////
	// mac states
	//

	protected byte getRetry() {
		if (null == packet)
			return 0; // no packet, no retry ...
		return (shouldRTS() ? longRetry : shortRetry);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see jist.swans.mac.MacDcf#resetRetry()
	 */
	protected void resetRetry() {
		shortRetry = 0;
		longRetry = 0;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see jist.swans.mac.MacDcf#isAwaitingResponse()
	 */
	protected boolean isAwaitingResponse() {
		switch (mode) {
			// MAC_MODE_SWFDATA causes problems while receiving DATA packets:
			// receiver thinks the incoming data is not for itself
			// case MAC_MODE_SWFDATA:
			case MAC_MODE_SWFCTS:
				return true;
			default:
				return super.isAwaitingResponse();
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see jist.swans.mac.MacDcf#isTransmitting()
	 */
	protected boolean isTransmitting() {
		switch (mode) {
			case MAC_MODE_XRTS:
			case MAC_MODE_XCTS:
				return true;
			default:
				return super.isTransmitting();
		}
	}

	// ////////////////////////////////////////////////
	// packet queries
	//

	/**
	 * Return whether current packet large enough to require RTS. TODO: Are
	 * there other reasons to request RTS/CTS? How/when is CTS-to-self
	 * triggered?
	 *
	 * @return does current packet require RTS.
	 */
	public boolean shouldRTS() {
		return shouldRTS(packet);
	}

	/**
	 * Return whether given packet is large enough to require RTS. TODO: Are
	 * there other reasons to request RTS/CTS? How/when is CTS-to-self
	 * triggered?
	 *
	 * @param msg
	 *            packet to be evaluated
	 * @return does current packet require RTS.
	 */
	public boolean shouldRTS(Message msg) {
		return msg.getSize() > macInfo.getThresholdRts() && !isBroadcast();
	}

	/**
	 * Return whether current packet requires fragmentation.
	 *
	 * @return does current packet require fragmentation.
	 */
	protected boolean shouldFragment() {
		return packet.getSize() > THRESHOLD_FRAGMENT && !isBroadcast();
	}

	// ////////////////////////////////////////////////
	// send-related functions
	//

	protected void sendPacket() {
		if (shouldRTS()) {
			sendRts();
		} else {
			sendData();
		}
	}

	protected void sendRts() {
	  /**
	     * function completely overridden in MacEDcf.
	     * Please propagate changes.
	     */
		// Do bit-rate selection according to algorithm.
		adjustBitrates(packet, anno, packetNextHop, true, msgFactory.getSize(
				Mac802_11Message.TYPE_RTS, this.packet, this.anno));

		// create rts packet
		long ctsTime = getSifs()
				+ transmitTime(msgFactory.getSize(Mac802_11Message.TYPE_CTS,
						this.packet, this.anno), macInfo.getBasicRateSet()[0])
				+ Constants.PROPAGATION;
		long dataTime = getSifs()
				+ transmitTime(msgFactory.getSize(Mac802_11Message.TYPE_DATA,
						this.packet, this.anno)
						+ packet.getSize(), dataRate) + Constants.PROPAGATION;
		long ackTime = getSifs()
				+ transmitTime(msgFactory.getSize(Mac802_11Message.TYPE_ACK),
						macInfo.getBasicRateSet()[0]) + Constants.PROPAGATION;
		long duration = ctsTime + dataTime + ackTime;

		Mac802_11Message.Rts rts = msgFactory.createRts(packetNextHop,
				localAddr, (int) duration, packet, anno);
		// set mode and transmit
		long delay = getRxTxTurnaround();

		// Add PHY layer information to the MAC message
		PhyMessage phyMessage = new PhyMessage(rts, controlRate, phy);
		long phyDuration = phyMessage.txDuration();

		// if (sendEvent.isActive())
		// sendEvent.handle(rts, anno, delay + duration +
		// Constants.EPSILON_DELAY, controlRate, false);

		radioEntity.transmit(phyMessage, anno, delay, phyDuration, Constants.TX_RX_TURNAROUND);

		// wait for EOT, schedule CTS wait timer
		JistAPI.sleep(delay);
		setMode(MAC_MODE_XRTS);

		if (sendEvent.isActive())
			sendEvent.handle(rts, anno, duration + Constants.EPSILON_DELAY,
					controlRate, false);

		JistAPI.sleep(phyDuration + Constants.TX_RX_TURNAROUND);
		// plus extra slot time to cover slightly larger delays
		self.startTimer(ctsTime + getSlotTime() + Constants.EPSILON_DELAY,
				MAC_MODE_SWFCTS);
	}

	protected void sendCts() {
		// Do bit-rate selection according to algorithm.
		adjustBitrates(rtsRcvd, rtsRcvdAnno, rtsRcvd.getSrc(), false,
				msgFactory.getSize(Mac802_11Message.TYPE_CTS, rtsRcvd,
						rtsRcvdAnno));

		// create cts packet
		long duration = rtsRcvd.getDuration()
				- getSifs()
				- transmitTime(msgFactory.getSize(Mac802_11Message.TYPE_CTS,
						this.rtsRcvd, this.rtsRcvdAnno), macInfo
						.getBasicRateSet()[0]) - Constants.PROPAGATION;
		Mac802_11Message.Cts cts = msgFactory.createCts(rtsRcvd.getSrc(),
				(int) duration, rtsRcvd, rtsRcvdAnno);

		// set mode and transmit
		long delay = getRxTxTurnaround();

		// Add PHY layer information to the MAC message
		PhyMessage phyMessage = new PhyMessage(cts, controlRate, phy);
		long phyDuration = phyMessage.txDuration();

		radioEntity.transmit(phyMessage, anno, delay, phyDuration, Constants.TX_RX_TURNAROUND);

		// wait for EOT, schedule DATA wait timer
		JistAPI.sleep(delay);
		setMode(MAC_MODE_XCTS);

		if (sendEvent.isActive())
			sendEvent.handle(cts, anno, duration + Constants.EPSILON_DELAY,
					controlRate, false);

		JistAPI.sleep(phyDuration + Constants.TX_RX_TURNAROUND);
		// plus extra slot time to cover slightly larger delays
		long endTimeOfDataRcpt = cts.getDuration()
				- getSifs()
				- transmitTime(msgFactory.getSize(Mac802_11Message.TYPE_ACK),
						controlRate) - Constants.PROPAGATION + getSlotTime()
				+ Constants.EPSILON_DELAY;
		self.startTimer(endTimeOfDataRcpt, MAC_MODE_SWFDATA);
	}

	// ////////////////////////////////////////////////
	// retry
	//
	// 802.11-1999.pdf, par. 9.2.5.3:
	// If the RTS transmission fails, the short retry count for the MSDU or
	// MMPDU and the STA short retry count are incremented. This process shall
	// continue until the number of attempts to transmit that MSDU or MMPDU
	// reaches dot11ShortRetryLimit.

	protected void retry() {
		// use long retry count for frames larger than RTS_THRESHOLD
		if (log.isDebugEnabled())
			log.debug(localAddr.addr + ": long retry:" + longRetry + " (of "
					+ macInfo.getRetryLimitLong() + ")");
		if (shouldRTS() && mode != MAC_MODE_SWFCTS) {
			if (longRetry < macInfo.getRetryLimitLong()) {
				longRetry++;
				if (retryEvent.isActive())
					retryEvent.handle(this.packet, this.anno, longRetry, false);
				retryYes();
			} else {
				setAnno(anno, MessageAnno.ANNO_MAC_RETRIES, new Byte(longRetry));
				if (macTxFinished.isActive())
					macTxFinished.handle(packet, anno, false, longRetry);
				longRetry = 0;
				retryNo();
			}
		} else {
			super.retry();
		}
	}

	// ////////////////////////////////////////////////
	// receive-related functions
	//

	// MacInterface
	public void peek(Message msg, MessageAnno anno) {
		super.peek(msg, anno);
		// TODO
		// if (mode == MAC_MODE_SNAV_RTS) {
		// idle();
		// }
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see jist.swans.mac.MacDcf#receivePacket(jist.swans.mac.MacDcfMessage,
	 *      jist.swans.misc.MessageAnno)
	 */
	protected void receivePacket(MacDcfMessage msg, MessageAnno anno) {
		needEifs = false;
		MacAddress dst = msg.getDst();

		if (localAddr.equals(dst) && msg.getType() == Mac802_11Message.TYPE_RTS)
			receiveRts((Mac802_11Message.Rts) msg, anno);
		else if (localAddr.equals(dst)
				&& msg.getType() == Mac802_11Message.TYPE_CTS)
			receiveCts((Mac802_11Message.Cts) msg, anno);
		else
			super.receivePacket(msg, anno);
	}

	protected void receiveRts(Mac802_11Message.Rts rts, MessageAnno anno) {
		/*
		 * NUGGET: if the cts is lost, resend the rts packet.
		 */
		if (nav.waitingNav() || mode == MAC_MODE_SWFACK /*
														 * || mode ==
														 * MAC_MODE_SWFCTS
														 */)
			return;
		if (mode == MAC_MODE_SWFDATA && !rtsRcvd.getSrc().equals(rts.getSrc()))
			return;

		if (receiveEvent.isActive())
			receiveEvent.handle(rts, anno, null == anno ? -1 : ((Long) anno
					.get(MessageAnno.ANNO_MAC_DURATION)).longValue()
					+ getRxTxTurnaround());

		cancelTimer();
		rtsRcvd = rts;
		rtsRcvdAnno = anno;
		/* start timer only after event handling, else the event(s) are delayed */
		startTimer(getTxSifs(), MAC_MODE_SIFS);

		if (null != dataRcvd)
			log.error(this + "(" + JistAPI.getTime() + "): dataRcvd="
					+ dataRcvd.toString());
	}

	protected void receiveCts(Mac802_11Message.Cts cts, MessageAnno anno) {
		if (mode == MAC_MODE_SWFCTS) {
			// RTS successful:
			rca.reportPacketTx(packetNextHop, Mac802_11Message.TYPE_RTS,
					msgFactory.getSize(Mac802_11Message.TYPE_RTS, this.packet,
							this.anno), controlRate, shortRetry + 1, 0,
					Constants.RADIO_RECV_STATUS_OK.byteValue());

			if (receiveEvent.isActive())
				receiveEvent.handle(cts, anno, null == anno ? -1 : ((Long) anno
						.get(MessageAnno.ANNO_MAC_DURATION)).longValue()
						+ getRxTxTurnaround());

			cancelTimer();
			// not in standard, but ns2 does
			// decCW();
			shortRetry = 0;
			// startTimer(getTxSifs(), MAC_MODE_SIFS);
			setMode(MAC_MODE_SIFS);
			JistAPI.sleep(getTxSifs());
			sendData();
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see jist.swans.mac.MacDcf#receiveData(jist.swans.mac.MacDcfMessage.Data,
	 *      jist.swans.misc.MessageAnno)
	 */
	protected void receiveData(MacDcfMessage.Data msg, MessageAnno anno) {
		// not in standard, but ns-2 does.
		// decCW();
		// shortRetry = 0;

		if (mode == MAC_MODE_SWFACK || mode == MAC_MODE_SWFCTS)
			return;

		// ignore broadcasts
		if (mode == MAC_MODE_SWFDATA && msg.getSrc().equals(rtsRcvd.getSrc())
				&& msg.getDst().equals(localAddr)) {
			// CTS successful:
			rca.reportPacketTx(msg.getSrc(), Mac802_11Message.TYPE_CTS,
					msgFactory.getSize(Mac802_11Message.TYPE_CTS, msg, anno),
					controlRate, shortRetry + 1, 0,
					Constants.RADIO_RECV_STATUS_OK.byteValue());
		}

		// not needed anymore
		rtsRcvd = null;
		rtsRcvdAnno = null;

		super.receiveData(msg, anno);
	}

	protected void reportDataTxToRca() {
		rca.reportPacketTx(packetNextHop, MacDcfMessage.TYPE_DATA, packet
				.getSize(), dataRate, shortRetry + 1, longRetry + 1,
				Constants.RADIO_RECV_STATUS_OK.byteValue());
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see jist.swans.mac.MacDcf#updateNav(jist.swans.mac.MacDcfMessage,
	 *      jist.swans.misc.MessageAnno)
	 */
	protected void updateNav(MacDcfMessage msg, MessageAnno anno) {
		MacAddress initiator = null;

		if (msg.getType() == Mac802_11Message.TYPE_RTS)
			initiator = ((Mac802_11Message.Rts) msg).getSrc();
		else if (msg.getType() == Mac802_11Message.TYPE_CTS)
			initiator = ((Mac802_11Message.Cts) msg).getDst();
		else {
			super.updateNav(msg, anno);
			return;
		}

		boolean changed = nav.setNav(JistAPI.getTime() + msg.getDuration()
				+ Constants.EPSILON_DELAY, initiator);

		if (changed && isRadioIdle() && hasPacket()) {
			// This is what we should do. TODO
			//
			// if (msg.getType()==MacDcfMessage.TYPE_RTS)
			// {
			// If RTS-ing node failed to get a CTS and start sending then
			// reset the NAV (MAC layer virtual carrier sense) for this
			// bystander node.
			// startTimer(Constants.PROPAGATION + getSifs() +
			// SYNCHRONIZATION +
			// MacDcfMessage.Cts.SIZE*Constants.SECOND/bandwidth +
			// Constants.PROPAGATION + 2*getSlotTime(),
			// MAC_MODE_SNAV_RTS);
			// }
			// else
			// {
			// startTimer(NAV - currentTime, MAC_MODE_SNAV);
			// }

			// This is for ns-2 comparison.
			startTimer(nav.getNav() - JistAPI.getTime(), MAC_MODE_SNAV);
		}
	}

	// ////////////////////////////////////////////////
	// radio mode
	//

	protected void radioBusy() {
		switch (mode) {
			case MAC_MODE_SWFCTS:
				/* AZu: support of RTS/CTS */
			case MAC_MODE_XCTS:
			case MAC_MODE_SWFDATA:
			case MAC_MODE_XRTS:
				// don't care
				break;
			default:
				super.radioBusy();
		}
	}

	protected void radioIdle() {
		switch (mode) {
			case MAC_MODE_SWFCTS:
				/* AZu: support of RTS/CTS */
			case MAC_MODE_XCTS:
			case MAC_MODE_XRTS:
			case MAC_MODE_SWFDATA:
				// don't care
				break;
			default:
				super.radioIdle();
		}
	}

	// ////////////////////////////////////////////////
	// timer routines
	//

	// MacInterface
	public void timeout(int timerId) {
		if (timerId != this.timerId)
			return;
		switch (mode) {
			case MAC_MODE_SIFS:
				if (null != rtsRcvd) {
					if (Main.ASSERT)
						Util.assertion(null == dataRcvd);
					sendCts();
				} else
					super.timeout(timerId);
				break;
			// case MAC_MODE_SNAV_RTS:
			// resetNav();
			// doDifs();
			// break;
			case MAC_MODE_SWFCTS:
				// RTS failed:
				// rca.reportPacketTx(packetNextHop, Mac802_11Message.TYPE_RTS,
				// msgFactory.getSize(Mac802_11Message.TYPE_RTS,
				// this.packet, this.anno), controlRate,
				// shortRetry + 1, 0, Constants.RADIO_RECV_STATUS_LOST
				// .byteValue());
				// Commented because: Report failure only once
				retry();
				break;
			case MAC_MODE_SWFDATA:
				// CTS failed: But don't know receiver address for CTS packet
				// sent!
				// rca.reportPacketTx(packetNextHop, MacDcfMessage.TYPE_CTS, -1,
				// MacDcfMessage.Cts.SIZE, controlRate, 1,
				// Constants.RADIO_RECV_STATUS_LOST.byteValue());
				setBackoff();
				doDifs();
				break;
			default:
				super.timeout(timerId);
		}
	}

}
