/* -*-	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 "packet.h"
#include "delay.h"
#include "mobilenode.h"
#include "address.h"

#include "arp.h"
#include "queue.h"
#include "cmu-trace.h"
#include "mac-umts.h"
#include "ll-ue.h"
#include <address.h>
#include "rlc-umts.h"

/******************************** Timers *********************************/
void AppsTimer::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 AppsTimer::stop(void)
{
	Scheduler &s = Scheduler::instance();

	if(paused_ == 0)
		s.cancel(&intr);

	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;
}
void AppsTimer::handle(Event *e)
{
	Scheduler &s = Scheduler::instance();
	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;

	ll->AppsHandler(flow_);
}

/////////////////////////////////////////////////////////////////////////////////////////
/*                          TUEList Implementation                                     */
/////////////////////////////////////////////////////////////////////////////////////////

TUEList:: ~TUEList()
{ TimerPtr temp = Head;
  CurrentPtr = Head;

  while(CurrentPtr != NULL)
  {CurrentPtr = CurrentPtr->Next;
   delete temp;
   temp=CurrentPtr;
  }
}

void TUEList::AddANode(int flow)
{Tail->Next = new AppsTimer(ll);
 Tail=Tail->Next;
 Tail->flow_ = flow;
 return;
}

TimerPtr TUEList::Previous(TimerPtr index)
{TimerPtr temp=Head;
 if(index==Head) //special case, index IS the head :)
  { return Head;
  }

 while(temp->Next != index)
 { temp=temp->Next;
 }
 return temp;
}

void TUEList::DeleteANode(TimerPtr corpse)
{ TimerPtr temp;

  if(corpse == Head) //case 1 corpse = Head
   {temp=Head;
    Head=Head->Next;
    delete temp;
   }
  else if(corpse == Tail) //case 2 corpse is at the end
  { temp=Previous(corpse);
    temp->Next=NULL;
    delete corpse;
    Tail=temp;
  }
  else //case 3 corpse is in middle somewhere
  {temp=Previous(corpse);
   temp->Next=corpse->Next;
   delete corpse;
  }

  CurrentPtr=Head; //Reset the class tempptr
}

void TUEList::DeleteANode(int flow)
{ TimerPtr temp, corpse;
  corpse = GetNode(flow);
  corpse->stop();
  if(corpse == Head) //case 1 corpse = Head
   {temp=Head;
    Head=Head->Next;
    delete temp;
   }
  else if(corpse == Tail) //case 2 corpse is at the end
  { temp=Previous(corpse);
    temp->Next=NULL;
    delete corpse;
    Tail=temp;
  }
  else //case 3 corpse is in middle somewhere
  {temp=Previous(corpse);
   temp->Next=corpse->Next;
   delete corpse;
  }

  CurrentPtr=Head; //Reset the class tempptr
}

TimerPtr TUEList::GetNode(int flow)
{ TimerPtr t=Head;
  while (t!=NULL){
  	if (t->flow_==flow){
		return t;
  	}
  	t=t->Next;
  }
  return NULL;
}

void TUEList::start(int flow, double time)
{ TimerPtr corpse;
  corpse = GetNode(flow);
  corpse->stop();
  corpse->start(time);
  return;
}

void TUEList::stop(int flow)
{ TimerPtr corpse;
  corpse = GetNode(flow);
  corpse->stop();
  return;
} 
/*********************************** Class LLUE ***********************************/

// TCL class
static class LLUEClass : public TclClass {
public:
	LLUEClass() : TclClass("LL/UE") {}
	TclObject* create(int, const char*const*) {
		return (new LLUE);
	}
} class_llue;

int LLUE::verbose_ = {0};

// constructor
LLUE::LLUE() : LL(), ue_state_(0), phy_(0), rlc_(0), ifq_(0)
{
	int i, j;

	bind("verbose_", &verbose_);
	ue_address_ = 0;
	for (i=0; i<MAX_NUM_FLOWS; i++) {
		q_[i] = new PacketQueue();
	}

	for (i=0; i<MAX_NUM_FLOWS; i++) {
		apps_[i].flowid_ = -1;
		apps_[i].dest_ = -1;
		apps_[i].wait_ = -1;
		apps_[i].sf_ = -1;
		apps_[i].rate_ = -1;
		apps_[i].user_rate_ = -1;
		apps_[i].interval_ = -1;
		apps_[i].c_coding_ = CONV_HALF;
	}

	handover_ = 0;
	freq_ = 0;
	appstimer_ = new TUEList(this);

}

int LLUE::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_);
			phy_->tti() = ifq_->tti();
			mac_->tti() = ifq_->tti();
			return (TCL_OK);
		}
		if (strcmp(argv[1], "rlc") == 0) {
			rlc_ = (RlcUmts*) TclObject::lookup(argv[2]);
                        assert(rlc_);
			return (TCL_OK);
		}
		if (strcmp(argv[1], "ifq") == 0) {
			ifq_ = (FCQueue*) TclObject::lookup(argv[2]);
                        assert(ifq_);
			return (TCL_OK);
		}
	}
	else if (argc == 2) {
		if (strcmp(argv[1], "phy") == 0) {
			tcl.resultf("%s", phy_->name());
			return (TCL_OK);
		}
		if (strcmp(argv[1], "rlc") == 0) {
			tcl.resultf("%s", rlc_->name());
			return (TCL_OK);
		}
		if (strcmp(argv[1], "ifq") == 0) {
			tcl.resultf("%s", ifq_->name());
			return (TCL_OK);
		}
	}
	return LL::command(argc, argv);
}


void LLUE::recv(Packet* p, Handler* /*h*/)
{
	int i, pos;
	hdr_cmn *ch = HDR_CMN(p);
	hdr_ll *lh = HDR_LL(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ip *ih = HDR_IP(p);

	Scheduler& s = Scheduler::instance();

	if (ch->direction() == hdr_cmn::UP){
		switch (ch->channel_t()) {
			case CCCH:	// CCCH logical channel
				if (lh->lltype() == LL_SETUP_REPLY) {
					// setup or handover procedure response
					ue_state_ = 2;	// change the UE state in RRC layer
					phy_->ue_state_ = 2;	// change the UE state in physical layer
					ue_address_ = phy_->ue_address_;  // take the UE physical address
					MobileNode * node_ = (MobileNode*)(phy_->netif_->node());
					ip_ue_ = node_->address();	// take the UE IP address
					phy_->ip_ue_ = ip_ue_;
					ifq_->ip_ue_ = ip_ue_;
					rlc_->addr() = ip_ue_;		// give the UE IP address to RLC layer
					nodeb_address_ = phy_->nodeb_address_; // take the Node B physical address
					ip_nodeb_ = ih->saddr();	// take the Node B IP address
					rlc_->ip_nodeb() = ip_nodeb_;	// give the Node B IP address to RLC layer
     					if (verbose_) printf("UE %d at %f RRC: SWITCH ON--------------\n", ip_ue_, NOW);
					Packet::free(p);
					p = NULL;
					break;
				} else if (lh->lltype() == LL_FAILURE) {
					if (ch->ptype() == PT_RRC_FAILURE) {
						// flow already running
						// error in the connection, flow refused!!!
						for (i=0; i<MAX_NUM_FLOWS; i++) {
							if ((ih->flowid() == apps_[i].flowid_) && (apps_[i].wait_ == 0)){
								// remove resources in RRC layer
								apps_[i].flowid_ = -1;
								apps_[i].wait_ = -1;
								apps_[i].rate_ = -1;
								apps_[i].sf_ = -1;
								apps_[i].user_rate_ = -1;
								apps_[i].c_coding_ = CONV_HALF;
								apps_[i].interval_ = -1;
								// remove the timer to the list of timers
								appstimer_->DeleteANode(ih->flowid());
								// delete flow in RLC layer
								rlc_->remove_flow(ih->flowid());
								// remove resources in IFQ
								ifq_->reg(ih->flowid(),0.0);
								// remove resources in physical layer
								update_phy_bytes_slot();
								// drop packets stored
								while (q_[i]->length() > 0) {
									Packet::free(p);
									p = NULL;
								}
								break;
							}
						}
					}
					// connection with nodeb has been impossible.
					phy_->handover_ = 0;	// end of handover procedure
					if (verbose_) printf("UE %d at %f RRC: Imposible to connect. UE out of coverage\n", ip_ue_, NOW);
					break;
				} else if (lh->lltype() == LL_RELEASE_REPLY) {
					// an application has finnished
					if (verbose_) printf("UE %d at %f RRC: LL_RELEASE_REPLY received\n", ip_ue_, NOW);
					remove_flow(p);	// remove resources
					return;
				} else if (lh->lltype() == LL_RELEASE_REQ) {
					// an application has finnished
					if (verbose_) printf("UE %d at %f RRC: LL_RELEASE_REQ received\n", ip_ue_, NOW);
					rlc_->remove_flow(ih->flowid());  // remove resources in RLC layer
					send_release_reply(p);		    // send the release reply message
					return;
				} else if (lh->lltype() == LL_RES_REPLY) {
					if (ch->ptype() != PT_RRC_FAILURE) {
						// resources allocated for that flow in Node B
						freq_ = ph->freq();	// take the new frequence
						// look for that flow and update arrays
						if (verbose_) printf("UE %d at %f RRC: LL_RES_REPLY received for allocating resources of flowid %d\n",
								 ip_ue_, NOW, ih->flowid());
						for (i=0; i<MAX_NUM_FLOWS; i++) {
							if ((ih->flowid() == apps_[i].flowid_) && (apps_[i].wait_ == 0)){
								// update fields
								apps_[i].wait_ = 1;
								double bytes = get_bytes(i);	// get bytes for IFQ
								ifq_->reg(ih->flowid(), bytes);	// register the flow in IFQ
								update_phy_bytes_slot();	// calculate bytes in physical layer
								// send the packets stored in the queue
								while (q_[i]->length() > 0) {
									send_msg(q_[i]->deque());
								}
								break;
							}
						}
					} else {
						// FAILURE! We don't have resources
						if (verbose_) printf("UE %d at %f RRC: LL_RES_REPLY FAILURE received for flowid %d\n",
								 ip_ue_, NOW, ih->flowid());
						for (i=0; i<MAX_NUM_FLOWS; i++) {
							if ((ih->flowid() == apps_[i].flowid_) && (apps_[i].wait_ == 0)){
								// remove resources in RRC layer
								apps_[i].flowid_ = -1;
								apps_[i].dest_ = -1;
								apps_[i].wait_ = -1;
								apps_[i].rate_ = -1;
								apps_[i].sf_ = -1;
								apps_[i].user_rate_ = -1;
								apps_[i].c_coding_ = CONV_HALF;
								apps_[i].interval_ = -1;
								// remove the timer to the list of timers
								appstimer_->DeleteANode(ih->flowid());
								// delete flow in RLC layer
								rlc_->remove_flow(ih->flowid());
								// drop packets stored
								while (q_[i]->length() > 0) {
									Packet::free(p);
									p = NULL;
								}
								break;
							}
						}
					}
				}
				Packet::free(p);
				p = NULL;
				break;
			case DTCH:  // dedicated data logical channel
				// data packet for the application has arrived, going up.
				if (verbose_) printf("UE %d at %f RRC: pkt arrived with uid:%d and size: %d, to the APPLICATION\n",
					ip_ue_, NOW, ch->uid(), ch->size());
				s.schedule(uptarget_, p, delay_);  // send the packet up through the scheduler
				break;
			case TO_RRC:	// directives from physical layer
				if (lh->lltype() == LL_PAGING) {
					// for paging procedure
					if (ph->da() == ue_address_) {	// check if the paging is for me
						hdr_rlc_umts *rh = HDR_RLC_UMTS(p);
						// check if we are in handover procedure
						if (handover_){
							rlc_->handover(1);  // update resources in RLC layer
							handover_ = 0;
						}
						// register the new flow in RLC layer
						update_flow(rh->ack(), rh->frag(), ih->flowid());
						// give the new downlink frequence to physical layer
						phy_->dlfreq = ph->freq();
						send_paging_ok(p); // send paging response
					}
					return;
				}
				if (lh->lltype() == LL_HANDOVER) {
					// for handover procedure
					phy_->handover_ = 1;  // update handover variable in physical layer
					handover_ = 1;	// update handover variable in RRC layer
					send_setup();	// begin cell search procedure
					Packet::free(p);
					p = NULL;
					return;
				}
				if (ch->ptype() == PT_RRC_FAILURE) {
					// It has been impossible to send the RACH message because
					//	- we have not received response to the preamble or
					//	- we have not found available resources to send the message
					for (i=0; i<MAX_NUM_FLOWS; i++) {
						if ((ih->flowid() == apps_[i].flowid_) && (apps_[i].wait_ == 0)){
							// remove resources in RRC layer
							apps_[i].flowid_ = -1;
							apps_[i].dest_ = -1;
							apps_[i].wait_ = -1;
							apps_[i].rate_ = -1;
							apps_[i].sf_ = -1;
							apps_[i].user_rate_ = -1;
							apps_[i].c_coding_ = CONV_HALF;
							apps_[i].interval_ = -1;
							// remove timer from the list of timers
							appstimer_->DeleteANode(ih->flowid());
							// delete flow in RLC layer
							rlc_->remove_flow(ih->flowid());
							// drop packets stored in the queue
							while (q_[i]->length() > 0) {
								Packet::free(p);
								p = NULL;
							}
							break;
						}
					}
					return;
				}
			default:
					Packet::free(p);
					p = NULL;
		}
		return;

	// direction DOWN
	} else {
		if (ue_state_ != 2){
			// the mobile is not already switched ON, drop packets
			if (verbose_) printf("UE %d at %f RRC: pkt dropped because UE is in state %d\n",ue_address_, ue_state_);
			Packet::free(p);
			p = NULL;
		} else {   // the mobile is switched ON
			i = detect_flow(p);  // process the packet
			if (i == 0) {
				// the flow has been refused, drop the packet
				if (verbose_) printf("UE %d at %f RRC: ERROR: Flow refused\n", ip_ue_, NOW);
				Packet::free(p);
				p = NULL;
			}
		}
	}
	return;
}

// calculate the needed spreading factor for a given rate
int get_sf(double rate)
{
	double index = rate * UMTS_SlotTime /8;  // bytes per slot in physical layer
	if (index > 0) {
		index = (320 / index);
		index = (int)(log(index)/log(2));
		int i, r, sf_ = 2;
		if (index > 9) index = 9;	// the highest spreading factor is 256
		if (index < 2) index = 2;	// the lowest spreading factor is 4
		for (i=0; i<((int)index - 1); i++) {
			sf_ *= 2;
		}
		return(sf_);
	} else
		return(0);
}

// check if the packet received is part of a current application
int LLUE::detect_flow(Packet* p)
{
	int i;
	hdr_ip *ih = HDR_IP(p);
	hdr_cmn *ch = HDR_CMN(p);
	// look for the flow
	for (i=0; i<MAX_NUM_FLOWS; i++){
		if (apps_[i].flowid_ == ih->flowid()){
			// flowid already registered
			if (apps_[i].wait_ == 1){
				// resources already allocated
				appstimer_->start(ih->flowid(), 1.0);   // start time
				send_msg(p->copy());		// send packet to IFQ
				return (1);
			} else if (apps_[i].wait_ == 0) {
				// flow waiting for resource reply
				appstimer_->start(ih->flowid(), 1.0);	// start time
				q_[i]->enque(p);		// store the packet in the queue until response
				return (1);
			} else {
				// is the first packet of that flow
				// calculate the physical rate that is going to send over the air interface
				apps_[i].rate_ = get_rate(apps_[i].user_rate_, apps_[i].c_coding_, ch->size());
				// calculate the interval between two packets
				apps_[i].interval_ = (ch->size() * 8)/ apps_[i].user_rate_;
				// add a timer for this flow in the list of timers
				appstimer_->AddANode(ih->flowid());
				appstimer_->start(ih->flowid(), 1.0);  // start timer
				// update field of the array apps_[]
				apps_[i].dest_ = ih->daddr();
				// spreading factor needed in physical layer
				apps_[i].sf_ = get_sf(apps_[i].rate_);
				send_res_req(p->copy(), i);  // send the resource request message
				apps_[i].wait_ = 0;   // put wait = 0 to prevent sending messages
				if (verbose_) printf("UE %d at %f RRC: STORING flow %d with physical rate %e and SF %d\n"
					,ip_ue_, NOW, ih->flowid(),apps_[i].rate_,apps_[i].sf_);
				q_[i]->enque(p->copy());  // store the packet in the queue until response
				return (1);
			}
		}
	}
	return (0);
}

// for release procedure: send a release reply message
void LLUE::send_release_reply(Packet* p)
{
	Scheduler& s = Scheduler::instance();
	Packet *sp = p->copy();
	hdr_ll *lh = HDR_LL(sp);
	hdr_ip *ih = HDR_IP(sp);
	hdr_phy *ph = HDR_PHY_UMTS(sp);
	hdr_cmn *ch = HDR_CMN(sp);

	// fill ip, common and physical headers
	ih->daddr() = ih->saddr();
	ih->saddr() = ip_ue_;
	lh->lltype() = LL_RELEASE_REPLY;
	ph->sa() = ue_address_;
	ph->da() = nodeb_address_;
	ch->direction() = hdr_cmn::DOWN;
	ch->channel_t() = CCCH;  // ch allocation
	if (verbose_) printf("UE %d at %f RRC: LL_RELEASE_REPLY sent related to the flowid %d\n",
		ip_ue_, NOW, ih->flowid());
	s.schedule(downtarget_, sp, delay_);  // send the packet down
	Packet::free(p);

	return;
}

// send data packets received from the application to the IFQ
void LLUE::send_msg(Packet* p)
{
	int i;
	Scheduler& s = Scheduler::instance();

	hdr_cmn *chp = HDR_CMN(p);
	hdr_ll *lhp = HDR_LL(p);
	hdr_ip *ihp = HDR_IP(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	// fill ll, physical and common headers
	lhp->lltype() = LL_DATA;
	for (i=0; i<MAX_NUM_FLOWS; i++) {
		if (ihp->flowid() == apps_[i].flowid_) {
			lhp->tx_rate() = apps_[i].rate_;  // puts the fisical rate in the packet
			break;
		}
	}

	php->c_coding() = apps_[i].c_coding_;  // channel coding
	chp->channel_t() = DTCH;  // ch allocation
	chp->next_hop_ = ip_nodeb_;
	chp->direction() = hdr_cmn::DOWN;
	chp->error() = 0;

	if (verbose_) printf("UE %d at %f RRC: sending DATA DOWN with uid: %d to destination(ip) %d and flow %d\n",  ip_ue_, NOW, chp->uid(), Address::instance().get_nodeaddr(ihp->daddr()), ihp->flowid());
	s.schedule(downtarget_, p, delay_);  // send the packet down through the scheduler

	return;
}

// for paging procedure: send the paging response
void LLUE::send_paging_ok(Packet* p)
{
	Scheduler& s = Scheduler::instance();

	hdr_ip *ih = HDR_IP(p);
	hdr_rlc_umts *rh = HDR_RLC_UMTS(p);
	Packet* sp = Packet::alloc();
	hdr_ip *ihsp = HDR_IP(sp);
	hdr_cmn *chsp = HDR_CMN(sp);
	hdr_ll *lhsp = HDR_LL(sp);
	hdr_phy *phsp = HDR_PHY_UMTS(sp);
	struct hdr_rlc_umts *rhsp = HDR_RLC_UMTS(sp);

	// fill physical, ll, rlc, common and ip headers
	phsp->sa() = ue_address_;
	phsp->da() = nodeb_address_;
	phsp->c_coding() = CONV_HALF;

	lhsp->lltype() = LL_PAGING;
	lhsp->paging_ok_ = 1;	// reponse ok

	rhsp->ack() = rh->ack();
	rhsp->frag() = rh->frag();

	chsp->channel_t() = CCCH;  // ch allocation
	chsp->next_hop_ = ip_nodeb_;
	chsp->direction() = hdr_cmn::DOWN;
	chsp->error() = 0;
	chsp->size() = 5;

	ihsp->flowid() = ih->flowid();
	ihsp->saddr() = ih->saddr();
	ihsp->daddr() = ih->daddr();

	if (verbose_) printf("UE %d at %f RRC: sending PAGING_OK\n", ip_ue_, NOW);
	s.schedule(downtarget_, sp, delay_);  // send the packet down through the scheduler

	Packet::free(p);
	p = NULL;
	return;
}

// calculate bytes per slot in physical layer and update them
void LLUE::update_phy_bytes_slot(void)
{
	int i;
	double bytes_slot, sum = 0.0;

	// add the physical rates of all the applications currently running
	for (i=0; i<MAX_NUM_FLOWS; i++) {
		if (apps_[i].flowid_ != -1) {
			sum += apps_[i].rate_;
		}
	}

	if (sum > 0) {
		// gets the spreading factor needed for supporting the total rate
		bytes_slot = (2560 / get_sf(sum)) / 8;	// calculate bytes in physical layer
		if (sum > 960000)		// if more than one dpdch is requiered
			bytes_slot = bytes_slot * ((int)(sum / 960000) + 1);
	} else {
		// no applications running in that moment
		bytes_slot = 0.0;
	}
 	if (verbose_) printf("UE %d at %f RRC: ~~~~~~~~~~~~~~~~~~~~~ bytes_slot in PHY %f ~~~~~~~~~~~~~~\n", ip_ue_, NOW, bytes_slot);
	phy_->bytes_slot = bytes_slot;  // update bytes per slot in physical layer
	return;
}

// register a new application in RRC layer
int LLUE::insert_flow(int flow, double rate, Chan_Coding code)
{
	int i;
	for (i=0; i<MAX_NUM_FLOWS; i++) {
		// looks for a free position
		if (apps_[i].flowid_ < 0){
			// update apps_[] fieds with the new information
			apps_[i].flowid_ = flow;
			apps_[i].wait_ = -1;
			apps_[i].user_rate_ = rate;
			apps_[i].c_coding_ = code;
			if (verbose_) printf("UE %d at %f RRC: trying to insert flow %d rate %f sf %d\n",
				 ip_ue_, NOW, apps_[i].flowid_, apps_[i].rate_, apps_[i].sf_);
			return(i);
		}
	}
	// there is not free position, ERROR
	return (-1);
}

// bytes in ifq per tti
double LLUE::get_bytes(int pos)
{
	double bits, sum;
	int rlcfsize = rlc_->rlcfragsize();		// rlc fragment size
	int rlchdrsize = RLC_HDR_SZ;			// rlc-umts header size
	int machdrsize = MAC_HDR_SZ;			// mac-umts header size
	int phyhdrsize = PHY_HDR_SZ;			// phy-umts header size

	bits = (2560 / apps_[pos].sf_) * (ifq_->tti() / UMTS_SlotTime);  // bits per tti
	sum = apps_[pos].rate_;
	if (sum > 0) {
		if (sum > 960000) // more than one dpdch is required
			bits = bits * ((int)(sum / 960000) + 1);
	}
	bits = (bits * (rlcfsize + rlchdrsize + machdrsize)) / (rlcfsize + rlchdrsize + machdrsize + phyhdrsize);

	// apply decoding
	switch (apps_[pos].c_coding_) {
		case TURBO:  // turbo coding
			bits = bits/3;
			break;
		case CONV_HALF:  // convolutional half
			bits = bits/2;
			break;
		case CONV_THIRD:   // convolutional third
			bits = bits/3;
			break;
		default:
			break;
	}
	// bytes in IFQ for an application per TTI
	bits = (bits * rlcfsize) / (rlcfsize + rlchdrsize + machdrsize);
 	if (verbose_) printf("UE %d at %f RRC: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bytes_tti in IFQ %f ~~~~~~~~~~~~~~\n", ip_ue_, NOW, bits/8);
	return(bits/8);  // bits in IFQ for that application per TTI
}

// calculate the physical rate
double LLUE::get_rate(double rate, Chan_Coding code, int psize)
{
	double rt, time;
	int rlcfsize = rlc_->rlcfragsize();		// rlc fragment size
	int rlchdrsize = RLC_HDR_SZ;			// rlc-umts header size
	int machdrsize = MAC_HDR_SZ;			// mac-umts header size
	int phyhdrsize = PHY_HDR_SZ;			// phy-umts header size


	time = (psize * 8)/ rate;		// time between two user packets
	int npkts = (int)(psize / rlcfsize);	// number of rlc fragments per user packet
	if ((psize % rlcfsize) > 0)
		npkts++;
	rt = (npkts * rlcfsize * 8)/time;	// effective user rate
	rt = (rt * (rlcfsize + rlchdrsize + machdrsize))/rlcfsize;	// user rate before channel coding
	// apply channel coding
	switch (code) {
		case 0:	// turbo coding
   			rt = 3*rt;
			break;
		case 1:	// convolutional half
   			rt = 2*rt;
			break;
		case 2:	// convolutional third
   			rt = 3*rt;
			break;
		default:
			break;
	}
	return(rt);
}

// send a resource request message
void LLUE::send_res_req(Packet* p, int i)
{
	Scheduler& s = Scheduler::instance();
	hdr_ip *ih = HDR_IP(p);
	hdr_cmn *ch = HDR_CMN(p);

	Packet* sp = Packet::alloc();
	struct hdr_cmn *chsp = HDR_CMN(sp);
	struct hdr_ll *lhsp = HDR_LL(sp);
	struct hdr_ip *ihsp = HDR_IP(sp);
	struct hdr_phy *phsp = HDR_PHY_UMTS(sp);

	// fill ll, common and ip headers
	lhsp->lltype() = LL_RES_REQ;
	lhsp->tx_rate() = apps_[i].rate_;
	lhsp->psize() = ch->size();
	lhsp->paging_ok_ = 0;

	phsp->c_coding() = CONV_HALF;
	chsp->channel_t() = CCCH;  // ch allocation
	chsp->next_hop_ = ip_nodeb_;
	chsp->direction() = hdr_cmn::DOWN;
	chsp->size() = 5;
	chsp->error() = 0;

	ihsp->flowid() = ih->flowid();
	ihsp->saddr() = ih->saddr();
	ihsp->daddr() = ih->daddr();

	if (verbose_) printf("UE %d at %f RRC: sending RESOURCE_REQUEST from ue(ip): %d to ue(ip): %d\n",  ip_ue_, NOW, Address::instance().get_nodeaddr(ihsp->saddr()),Address::instance().get_nodeaddr(ihsp->daddr()));
	s.schedule(downtarget_, sp->copy(), delay_);  // send the packet down through the scheduler

	Packet::free(sp);
	sp = NULL;
	return;
}

// for setup and handover procedures: send a setup request message
void LLUE::send_setup(void)
{
	Packet* sp = Packet::alloc();
	struct hdr_cmn *chsp = HDR_CMN(sp);
	struct hdr_ll *lhsp = HDR_LL(sp);
	struct hdr_ip *ihsp = HDR_IP(sp);
	struct hdr_phy *phsp = HDR_PHY_UMTS(sp);

	MobileNode * node_ = (MobileNode*)(phy_->netif_->node());
	Scheduler& s = Scheduler::instance();

	// fill physical, ll, common and ip headers
	chsp->direction_ = hdr_cmn::DOWN;

	lhsp->tx_rate() = 0;

	chsp->size_=32;
	chsp->error()= 0;
	chsp->channel_t()=CCCH;  // ch allocation
	chsp->next_hop_= -1;

	phsp->c_coding() = CONV_HALF;

	int k, i = 0;
	for (k=0; k<MAX_NUM_FLOWS; k++){
		phsp->flows_[k][0] = new int;
		phsp->flows_[k][1] = new int;
		phsp->flows_[k][2] = new int;
		*phsp->flows_[k][0] = -1;
		*phsp->flows_[k][1] = -1;
		*phsp->flows_[k][2] = -1;
	}
	if (handover_){
		// handover procedure
		for (k=0; k<MAX_NUM_FLOWS; k++){
			if (apps_[k].flowid_ > 0){
				// inform the new Node B about the flows already running, and their mode
				*phsp->flows_[i][0] = rlc_->get_acked(apps_[k].flowid_);
				*phsp->flows_[i][1] = rlc_->get_fraged(apps_[k].flowid_);
				*phsp->flows_[i][2] = apps_[k].flowid_;
				i++;
			}
		}
	}


	ihsp->saddr() = node_->address();

	lhsp->lltype() = LL_SETUP;
	if (verbose_) printf("UE %d at %f RRC: Trying to Switched ON......\n",  ip_ue_, NOW);
	s.schedule(downtarget_, sp, delay_);  // send the packet down through the scheduler

	return;
}

// for switching ON the mobile
void LLUE::switch_on(void)
{
	ue_state_ = 1;	// change the state in RRC from 0 to 1
	phy_->ue_state_ = 1;	// change the state in physical layer from 0 to 1
	send_setup();	// begin cell search procedure
	return;
}

// register a new flow in RLC layer
void LLUE::update_flow(int acked, int fraged, int flowid)
{
	if (verbose_) printf("UE %d at %f RRC: Update_flow: ack, frag, flowid [%d %d %d]\n", ip_ue_, NOW, acked, fraged, flowid);
	rlc_->store_flow(acked, fraged, flowid);   // register the flow with its characteristics
	return;
}

// configure a new flow in RRC layer
void LLUE::configure_flow(int flow, const char*const& type , double rate)
{

	int acked, fraged;
	Chan_Coding code;

	// depending on the type of application, chose the mode (ACK or non-ACK),
	// the channel coding and if is necessary fragmentation (default fragmentation)
	if (strcmp(type, "audio") == 0) {
		code = TURBO;	// turbo coding
		fraged = 1;		// fragmentation
		acked = 0;		// non-ACK mode
	}
	if (strcmp(type, "video") == 0) {
		code = TURBO;	// turbo coding
		fraged = 1;		// fragmentation
		acked = 0;		// non-ACK mode
	}
	if (strcmp(type, "mail") == 0) {
		code = CONV_THIRD;	// convolutional third
		fraged = 1;		// fragmentation
		acked = 1;		// ACK mode
	}
	if (strcmp(type, "fax") == 0) {
		code = CONV_HALF;	// convolutional half
		fraged = 1;		// fragmentation
		acked = 0;		// non-ACK mode
	}
	if (strcmp(type, "speech") == 0) {
		code = CONV_HALF;	// convolutional half
		fraged = 1;		// fragmentation
		acked = 0;		// non-ACK mode
	}
	if (strcmp(type, "ftp") == 0) {
		code = TURBO;	// turbo coding
		fraged = 1;		// fragmentation
		acked = 1;		// ACK mode
	}
	if (strcmp(type, "http") == 0) {
		code = CONV_HALF;	// convolutional half
		fraged = 1;		// fragmentation
		acked = 1;		// ACK mode
	}

	// register the new flow with its characteristics in the apps_[] array
	int i = insert_flow(flow, rate, code);
	if (i == -1) {
		// too many applications in this terminal
		if (verbose_) printf("UE %d at %f RRC: ERROR: Trying to allocate in UE %d more than the MAX_NUM_FLOWS %d permissible\n"
			,ip_ue_, NOW, MAX_NUM_FLOWS);
		exit(-1);
	}
	// register the new flow in RLC layer
	update_flow(acked, fraged, flow);
	return;
}

// remove resources allocated for a flow
void LLUE::remove_flow(Packet* p)
{
	int i;
	hdr_ip *ih = HDR_IP(p);
	// looks for the flow in the array
	for (i=0; i<MAX_NUM_FLOWS; i++) {
		if (apps_[i].flowid_ == ih->flowid()){
			break;
		}
	}
	rlc_->remove_flow(apps_[i].flowid_);	// remove resources from RLC
	ifq_->reg(apps_[i].flowid_,0.0);		// remove resources from IFQ
	// remove resources from RRC layer
	apps_[i].flowid_ = -1;
	apps_[i].dest_ = -1;
	apps_[i].wait_ = -1;
	apps_[i].sf_ = -1;
	apps_[i].rate_ = -1;
	apps_[i].user_rate_ = -1;
	apps_[i].interval_ = -1;
	apps_[i].c_coding_ = CONV_HALF;

	update_phy_bytes_slot();			// remove resources from PHY

	Packet::free(p);
	p = NULL;

	return;

}

// begin the release procedure
void LLUE::AppsHandler(int flow)
{
	// The application has finished;
	// we have to send a release request to the Node B and to the node destiny
	Scheduler& s = Scheduler::instance();
	int pos;
	// look for the flow
	for (pos=0; pos<MAX_NUM_FLOWS; pos++) {
		if (apps_[pos].flowid_ == flow)
			break;
	}

	Packet* sp = Packet::alloc();
	struct hdr_cmn *chsp = HDR_CMN(sp);
	struct hdr_ll *lhsp = HDR_LL(sp);
	struct hdr_ip *ihsp = HDR_IP(sp);
	struct hdr_phy *phsp = HDR_PHY_UMTS(sp);

	// fill physical, ll, common and ip headers
	lhsp->lltype() = LL_RELEASE_REQ;
	lhsp->tx_rate() = apps_[pos].rate_;
	lhsp->paging_ok_ = 0;

	phsp->sa() = ue_address_;
	phsp->da() = nodeb_address_;
	phsp->c_coding() = CONV_HALF;

	chsp->channel_t() = CCCH;  // ch allocation
	chsp->next_hop_ = ip_nodeb_;
	chsp->direction() = hdr_cmn::DOWN;
	chsp->size() = 5;
	chsp->error() = 0;

	ihsp->flowid() = apps_[pos].flowid_;
	ihsp->saddr() = ip_ue_;
	ihsp->daddr() = apps_[pos].dest_;

	if (verbose_) printf("\nUE %d at %f RRC: ******************* sending RELEASE_REQ to ue(ip): %d\n",  ip_ue_, NOW, Address::instance().get_nodeaddr(ihsp->daddr()));
	s.schedule(downtarget_, sp->copy(), delay_);   // send the packet down through the scheduler

	Packet::free(sp);
	sp = NULL;

	return;
}








