/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */

/* By Pablo Martin and Paula Ballester,
 * Strathclyde University, Glasgow.
 * June, 2003.
*/

/* Copyright (c) 2003 Strathclyde University of Glasgow, Scotland.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code and binary code must contain
 * the above copyright notice, this list of conditions and the following
 * disclaimer.
 *
 * 2. All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 * This product includes software developed at Strathclyde University of
 * Glasgow, Scotland.
 *
 * 3. The name of the University may not be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 * STRATHCLYDE UNIVERSITY OF GLASGOW, MAKES NO REPRESENTATIONS
 * CONCERNING EITHER THE MERCHANTABILITY OF THIS SOFTWARE OR THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software
 * is provided "as is" without express or implied warranty of any kind.
*/

#include "delay.h"
#include "connector.h"
#include "packet.h"
#include "random.h"
#include "mobilenode.h"
#include "address.h"

#include "arp.h"
#include "ll.h"
#include "queue.h"
#include "mac.h"
#include "mac-umts.h"
#include "phy-umts.h"
#include "cmu-trace.h"

/* Timers */
void MacUmtsTimer::start(Packet *p, double time)
{
	Scheduler &s = Scheduler::instance();
	assert(busy_ == 0);
 
	busy_ = 1;
	paused_ = 0;
	stime = s.clock();
	rtime = time;
	assert(rtime >= 0.0);

	s.schedule(this, p, rtime);
}

void MacUmtsTimer::stop(Packet *p)
{
	Scheduler &s = Scheduler::instance();
	assert(busy_);

	if(paused_ == 0)
		s.cancel((Event *)p);

	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;
}

void WaitTimer::start(double time)
{
	Scheduler &s = Scheduler::instance();
	assert(busy_ == 0);
	busy_ = 1;
	paused_ = 0;
	stime = s.clock();
	rtime = time;
	assert(rtime >= 0.0);
	s.schedule(this, &intr, rtime);
}

void WaitTimer::stop(void)
{
	Scheduler &s = Scheduler::instance();
	assert(busy_);
	if(paused_ == 0)
		s.cancel(&intr);
	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;
}

void WaitTimer::handle(Event *e)
{
	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;
	mac->waitHandler();
}

// TCL Class
static class MacUmtsClass : public TclClass {
public:
	MacUmtsClass() : TclClass("Mac/Umts") {}
	TclObject* create(int, const char*const*) {
		return (new MacUmts());
	}
} class_mac_umts;

int MacUmts::verbose_ = {0};

//constructor
MacUmts::MacUmts() : Mac(), mhwait_(this)
{
	int i,j;
	bind("verbose_", &verbose_);
	mhwait_.start(0);	// start timer

	error_ = 0;			// not error
	dl_error = 1000000000;	// null error probability
}

int MacUmts::command(int argc, const char*const* argv)
{
	Tcl& tcl = Tcl::instance();
	if (argc == 3) {
		if (strcmp(argv[1], "phy") == 0) {
			phy_ = (PhyUmts*) TclObject::lookup(argv[2]);
                        assert(phy_);
			return (TCL_OK);
		}
	}
	else if (argc == 2) {
		if (strcmp(argv[1], "phy") == 0) {
			tcl.resultf("%s", phy_->name());
			return (TCL_OK);
		}
	}
	return Mac::command(argc, argv);
}

/* To handle incoming packet. */
void MacUmts::recv(Packet* p, Handler* h)
{
	struct hdr_cmn *ch = HDR_CMN(p);
	
	int i,j,len;

	switch (ch->channel_t()){
		case PCH:
			ch->channel_t() = PCCH;
			ch->size() -= MAC_HDR_SZ;
			uptarget_->recv(p, this);
			break;
		case FACH:
			ch->channel_t() = CCCH;
			ch->size() -= MAC_HDR_SZ;
			uptarget_->recv(p, this);
			break;
		case DCH:
			ch->channel_t() = DTCH;
			ch->size() -= MAC_HDR_SZ;
			sendpkt(p);		// check if error
			break;
		case RACH:
			ch->channel_t() = CCCH;
			ch->size() -= MAC_HDR_SZ;
			uptarget_->recv(p, this);
			break;
		case PCCH:
			ch->channel_t() = PCH;
			ch->size() += MAC_HDR_SZ;
			downtarget_->recv(p, this);
			break;
		case CCCH:
			ch->channel_t() = RACH;
			ch->size() += MAC_HDR_SZ;
			downtarget_->recv(p, this);
			break;
		case DTCH:
			ch->channel_t() = DCH;
			ch->size() += MAC_HDR_SZ;
			downtarget_->recv(p, this);
			break;
 		default:
			if (ch->direction() == hdr_cmn::UP){
				ch->size() -= MAC_HDR_SZ;
				uptarget_->recv(p, this);
			} else {
				ch->size() += MAC_HDR_SZ;
				downtarget_->recv(p, this);
			}
	}
	return;
}

// for passing results from PHY
void MacUmts::dl_interference(unsigned long error)
{
	dl_error = error; 	// update dl_error
	return;
}

// returns if the actual transport block is erroneous
int MacUmts::check_error()
{
	int n = Random::integer(dl_error);	// generate random number based on dl_error probability
	if (n == 0) {		// error
		if (verbose_==1)
			printf("\nUE %d at %f MAC: ERROR in transport block____________________________________\n\n",
				phy_->ip_ue_, NOW);
		return(1); //ERROR
	}
	return(0);	// not error
}

// if erroneous packet, drop it, if not, send it
void MacUmts::sendpkt(Packet* p)
{
	if (error_) {
		drop(p);
	} else {
		uptarget_->recv(p, this);
	}
	return;
}

// handler of class WaitTimer, each tti_
void MacUmts::waitHandler()
{
	mhwait_.start(tti_);	// schedule the next tti_
	error_ = check_error();	// update error_
	return;
}


