/* -*-	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-nodeb.h"
#include "ll-nodeb.h"
#include "rlc-umts.h"
#include <address.h>

/******************************** Timers *********************************/

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

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

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

	ll->RemoveFlowHandler(src_, dest_, size_, flow_);
}


////////////////////////// TNodebList Implementation ///////////////////////////////////////


TNodebList:: ~TNodebList()
{ TimerNodebPtr temp = Head;
  CurrentPtr = Head;

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

void TNodebList::AddANode(nsaddr_t src, nsaddr_t dest, int size, int flow)
{Tail->Next = new AppsNodebTimer(ll);
 Tail=Tail->Next;
 Tail->src_ = src;
 Tail->dest_ = dest;
 Tail->size_ = size;
 Tail->flow_ = flow;
 return;
}

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

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

void TNodebList::DeleteANode(TimerNodebPtr corpse)
{ TimerNodebPtr 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 TNodebList::DeleteANode(nsaddr_t src, int flow)
{ TimerNodebPtr temp, corpse;
  corpse = GetNode(src, 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
}

TimerNodebPtr TNodebList::GetNode(nsaddr_t src, int flow)
{ TimerNodebPtr t=Head;
  while (t!=NULL){
  	if ((t->flow_==flow) && (t->src_==src)){
		return t;
  	}
  	t=t->Next;
  }
  return NULL;
}

void TNodebList::start(nsaddr_t src, int flow, double time)
{ TimerNodebPtr corpse;
  corpse = GetNode(src, flow);
  corpse->stop();
  corpse->start(time);
  return;
}

void TNodebList::stop(nsaddr_t src, int flow)
{ TimerNodebPtr corpse;
  corpse = GetNode(src, flow);
  corpse->stop();
  return;
}
///////////////////////////////////////////////////////////////////////////////////////


void ReleaseUETimer::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 ReleaseUETimer::stop()
{
	Scheduler &s = Scheduler::instance();
	assert(busy_);

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

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

void ReleaseUETimer::handle(Event *e)
{
	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;
	ll->RemoveUEHandler(addr_);
}

////////////////////////// RTNodebList Implementation ///////////////////////////////////////

RTNodebList:: ~RTNodebList()
{ RTimerNodebPtr temp = Head;
  CurrentPtr = Head;

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

void RTNodebList::AddANode(int addr)
{Tail->Next = new ReleaseUETimer(ll);
 Tail=Tail->Next;
 Tail->addr_ = addr;
 return;
}

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

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

void RTNodebList::DeleteANode(RTimerNodebPtr corpse)
{ RTimerNodebPtr 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 RTNodebList::DeleteANode(int addr)
{ RTimerNodebPtr temp, corpse;
  corpse = GetNode(addr);
  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
}

RTimerNodebPtr RTNodebList::GetNode(int addr)
{ RTimerNodebPtr t=Head;
  while (t!=NULL){
  	if (t->addr_==addr){
		return t;
  	}
  	t=t->Next;
  }
  return NULL;
}

void RTNodebList::start(int addr, double time)
{ RTimerNodebPtr corpse;
  corpse = GetNode(addr);
  corpse->stop();
  corpse->start(time);
  return;
}

void RTNodebList::stop(int addr)
{ RTimerNodebPtr corpse;
  corpse = GetNode(addr);
  corpse->stop();
  return;
}

///////////////////////////// Class LLNodeb ///////////////////////////////////


// TCL class
static class LLNodebClass : public TclClass {
public:
	LLNodebClass() : TclClass("LL/Nodeb") {}
	TclObject* create(int, const char*const*) {
		return (new LLNodeb);
	}
} class_llNodeb;

// static structures
struct location LLNodeb::registry_[MAX_NUM_NODES] = {0};

int LLNodeb::verbose_ = {0};

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

	bind("verbose_", &verbose_);

	for (i=0; i<MAX_NUM_NODES; i++) {
		registry_[i].ue_ = -1;
		registry_[i].nodeb_ = -1;
	}

	for (i=0; i<MAX_NUM_UE; i++) {
		ue_info_[i].ipaddr_ = -1;
		ue_info_[i].phyaddr_ = -1;
		ue_info_[i].dltempsf_ = -1;
		ue_info_[i].dlsf_ = -1;
		ue_info_[i].dlfreq_ = -1;
		ue_info_[i].dltotalr_ = 0;
	}

	refused = new linked_list;
	flows_ = new FlowList;
	removeflow_ = new TNodebList(this);
	remove_ = new RTNodebList(this);
	fl_= new PacketQueue();

	for (i=0; i< MAX_NUM_FREQ; i++) {
		dl_sftree[i] = new tree(512);
	}

}

int LLNodeb::command(int argc, const char*const* argv)
{
	Tcl& tcl = Tcl::instance();
	if (argc == 3) {
		if (strcmp(argv[1], "phy") == 0) {
			phy_ = (PhyUmtsNodeb*) TclObject::lookup(argv[2]);
                        assert(phy_);
			phy_->tti() = ifq_->tti();
			mac_->tti() = ifq_->tti();
			return (TCL_OK);
		}
		if (strcmp(argv[1], "ifq") == 0) {
			ifq_ = (BsFCQueue*) TclObject::lookup(argv[2]);
                        assert(ifq_);
			return (TCL_OK);
		}
		if (strcmp(argv[1], "rlc") == 0) {
			rlc_ = (RlcUmtsNodeb*) TclObject::lookup(argv[2]);
                        assert(rlc_);
			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], "ifq") == 0) {
			tcl.resultf("%s", ifq_->name());
			return (TCL_OK);
		}

		if (strcmp(argv[1], "on") == 0) {
			MobileNode * node_ = (MobileNode*)(phy_->netif_->node());
			ip_nodeb_ = node_->address();
			rlc_->ip_nodeb() = ip_nodeb_;
			ifq_->ip_nodeb_ = ip_nodeb_;
			phy_->ip_nodeb_ = ip_nodeb_;
			return (TCL_OK);
		}
		if (strcmp(argv[1], "rlc") == 0) {
			tcl.resultf("%s", rlc_->name());
			return (TCL_OK);
		}
	}
	return LL::command(argc, argv);
}

// get the IP address
int LLNodeb::getip(nsaddr_t addr)
{
	return (Address::instance().get_nodeaddr(addr));
}


void LLNodeb::recv(Packet* p, Handler* /*h*/)
{
	int i, j, temp;
	hdr_cmn *ch = HDR_CMN(p);
	hdr_ll *lh = HDR_LL(p);
	struct hdr_ip *ihp = HDR_IP(p);
	hdr_rlc_umts *rh = HDR_RLC_UMTS(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	FListPtr ptr;

	assert(initialized());
	Scheduler& s = Scheduler::instance();

	if (ch->direction() == hdr_cmn::UP){
		switch (ch->channel_t()){
			case CCCH:
				// received setup request message, in setup or handover procedures
				if (lh->lltype() == LL_SETUP) {
					// try to register the UE
					if (register_ue(p)) {
						// UE register in RRC layer
						update_db(p);	// update the static location matrix
						send_setup_reply(p);	// send the setup response message
						return;
					}
					// it has been impossible to register the UE
					// send a failure message
					if (verbose_) printf("Nodeb %d at %f RRC: LL_SETUP received. FAILURE, not enough resources for UE %d\n"
						,ip_nodeb_, NOW, ue_info_[i].ipaddr_);
					lh->lltype() = LL_FAILURE;
					ch->direction() = hdr_cmn::DOWN;
					ch->next_hop() = ihp->saddr();
					s.schedule(downtarget_, p->copy(), delay_);  // send the packet down through the scheduler
					return;
				}

				// received release request message, in release procedure
				if (lh->lltype() == LL_RELEASE_REQ) {
					if (verbose_) printf("Nodeb %d at %f RRC: LL_RELEASE_REQ received\n", ip_nodeb_, NOW);
					// check if the node destiny is a mobile node
					if (ue_node(ihp->daddr())) {
						// the destination is a moble, check if is in my cell
						i = look_for(ihp->daddr());
						if (i != -1) {
							// the destination is in my cell
							// fill physical and common headers
							ph->sa() = phy_->nodeb_address_;
							ph->da() = ue_info_[i].phyaddr_;
							ch->channel_t() = CCCH;	// ch allocation
							ch->direction() = hdr_cmn::DOWN;
							ch->next_hop() = ihp->saddr();
							// send the release request message to the node destiny down through the scheduler
							s.schedule(downtarget_, p->copy(), delay_);
						} else {
							// the destination is not in my cell
							ch->channel_t() = FROM_RRC;	// ch allocation
							ch->direction() = hdr_cmn::UP;
							send_to_ra(p);	// send the packet to the routing agent
						}
					} else {
						// the destination is a fixed node
						i = look_for(ihp->saddr());
						remove_flow_ul(p,i);	// remove the uplink resources allocated for that flow
						ph->da() = ue_info_[i].phyaddr_;
						send_release_reply(p);	// send release reply message
					}
					return;
				}

				// received release reply message, in release procedure
				if (lh->lltype() == LL_RELEASE_REPLY) {
					if (verbose_) printf("Nodeb %d at %f RRC: LL_RELEASE_REPLY received from ip %d to %d\n",
						ip_nodeb_, NOW, ihp->saddr(), ihp->daddr());
					remove_flow_dl(p->copy());	// remove downlink resources allocated for that flow
					// check if the release procedure initiator is a mobile
					i = look_for(ihp->daddr());
					if (i != -1) {
						// the release_req initiator is a UE in my cell
						remove_flow_ul(p,i);	// remove the uplink resources allocated for that flow
						ph->da() = ue_info_[i].phyaddr_;
						send_release_reply(p);	// send release reply message
					} else if (ue_node(ihp->daddr())){
						// the release_req initiator is a UE in other cell
						ch->channel_t() = FROM_RRC;	// ch allocation
						ch->direction() = hdr_cmn::UP;
						send_to_ra(p);	// send the packet to the routing agent
					} else {
						// the release_req initiator is a fixed node
						// Delete the timer from the list of timers
						removeflow_->DeleteANode(ihp->daddr(), ihp->flowid());
					}
					return;
				}

				// received resource request message
				if (lh->lltype() == LL_RES_REQ) {
					if (verbose_) printf("Nodeb %d at %f RRC: RES_REQ received from ue(ip) %d, to ue(ip) %d\n",
						ip_nodeb_, NOW, getip(ihp->saddr()), getip(ihp->daddr()));
					// look for the node destiny
					i = look_for(ihp->daddr());
					if (i != -1) {
						// the destination is in my cell
						if (free_res(p)) {
							// there are enough resources in the downlink
							update_flow(rh->ack(), rh->frag(), ihp->flowid(), ihp->saddr(),0);
							pag_proc(p,i);	// begin paging procedure
						} else {
							// there are not enough resources in the downlink
							send_res_reply(p, 0); // send a failure
						}
					} else {
						// the UE destination is not in my cell
						for (j=0; j<MAX_NUM_NODES; j++){
							if (registry_[j].ue_ == ihp->daddr()) {
								// send the request to other Node B
								send_to_ra(p->copy());
								Packet::free(p);
								break;
							}
						}
						// is a fixed node, give resources
						if (j == MAX_NUM_NODES) {
							update_flow(rh->ack(), rh->frag(), ihp->flowid(), ihp->saddr(),0);
							send_res_reply(p, 1);	// send the resource reply message
						}
					}
					return;
				}

				// paging response received
				if (lh->lltype() == LL_PAGING){
					i = look_for(ihp->daddr());
					if (verbose_) printf("Nodeb %d at %f RRC: PAGING response received from UE %d\n"
						,ip_nodeb_, NOW, ihp->daddr());
					ifq_->reg(ihp->daddr(), get_bytes(i)); // registring in IFQ
					update_phy_bytes_slot(i);	// updating physical bytes
					update_flow(rh->ack(), rh->frag(), ihp->flowid(), ihp->daddr(),0);
					if (ue_node(ihp->saddr())) {
						// the origin is a mobile node
						send_res_reply(p->copy(), 1);	// send the resource reply message
					}
					ue_info_[i].dltempsf_ = -1;
					// go through the queue sending this flow pkts
					ptr = flows_->GetNode(ihp->saddr(), ihp->daddr(), ihp->flowid());
					if (ptr != NULL) {
						flows_->wait(ihp->saddr(), ihp->daddr(), ihp->flowid(), 0);
						temp = fl_->length();
						while (temp > 0) {
							Packet* prov;
							prov = fl_->deque();
							struct hdr_ip *ihprov = HDR_IP(prov);
							if ((ihprov->flowid() == ihp->flowid()) && (ihprov->daddr() == ihp->daddr())){
								// packet belonging to that flow
								send_msg(prov->copy(),i);
							}else{
								// packet belonging to other flow
								fl_->enque(prov->copy());
							}
							--temp;
							Packet::free(prov);
						}
						Packet::free(p);
						p = NULL;
						return;
					}
				}
				break;

			case DTCH:	
				// data packet arrived
				if (ihp->daddr() != ip_nodeb_) {
					i = look_for(ihp->daddr());
					if (i != -1) {
						// the destiny is in my cell
						ph->sa() = phy_->nodeb_address_;
						ph->da() = ue_info_[i].phyaddr_;
						ch->direction() = hdr_cmn::DOWN;
						if (verbose_) printf("\nNodeb %d at %f RRC: DTCH to UE(phy): %d\n",
							ip_nodeb_, NOW,ph->da());
						ch->next_hop_ = ue_info_[i].ipaddr_;
						ch->prev_hop_ = ip_nodeb_;
						s.schedule(downtarget_, p->copy(), delay_);  // send the packet down through the scheduler
						Packet::free(p);
						p = NULL;
						return;
					}
				}
				// packet for me or for a mobile in other cell or for a fixed node
				if (verbose_) printf("Nodeb %d at %f RRC: sending packet UP with uid:%d and size %d\n",
					ip_nodeb_, NOW, ch->uid(), ch->size());
				s.schedule(uptarget_, p, delay_); // send the packet up through the scheduler
				break;

			case TO_RRC:	// directives from the physical layer
				if (ch->ptype() == PT_RRC_FAILURE) {
				// x.e. no paging response
					refused->add(ihp->flowid());
					int dest = look_for(ihp->daddr());
					// remove resources allocated for that flow from OSVF trees
					remove_res(dest);
					if (ue_info_[dest].dltempsf_ > 0){
						// re-allocate resources
						alloc_res(p,ue_info_[dest].dltempsf_);
						ue_info_[dest].dltempsf_ = -1;
						ue_info_[dest].dltotalr_ -= lh->tx_rate();
					}
					if (ue_node(ihp->saddr())) {
						// the source is a mobile node
						send_res_reply(p->copy(),0);	// send a failure
					} else {
						// Delete the timer from the list of timers
						removeflow_->DeleteANode(ihp->saddr(), ihp->flowid());
					}
					flows_->DeleteANode(ihp->saddr(), ihp->daddr(), ihp->flowid());
					// goes through the queue dropping this flow packets
					temp = 0;
					if (fl_->length() > 0) {
						temp = fl_->length();
						while (temp > 0) {
							Packet* prov = fl_->deque();
							struct hdr_ip *ihprov = HDR_IP(prov);
							if ((ihprov->flowid() == ihp->flowid()) && (ihprov->daddr() == ihp->daddr())){
								Packet::free(prov);
								prov = NULL;
							} else {
								// the packet does not belong to this flow
								fl_->enque(prov);
							}
							temp--;
						}
					}
				}
				Packet::free(p);
				p = NULL;
				break;
			default:
				Packet::free(p);
				p = NULL;
				break;
		}

	///////// direction: DOWN /////////////////

	} else {
	switch (ch->channel_t()){
		case FROM_RRC:	// packet received from other Node B
			// Resource reply message received from other cell
			if (lh->lltype() == LL_RES_REPLY) {
				nsaddr_t prov = ihp->daddr();
				ihp->daddr() = ihp->saddr();
				ihp->saddr() = prov;
				if (ch->ptype() != PT_RRC_FAILURE) {
					// reply not failure
					if (verbose_) printf("Nodeb %d at %f RRC: RES_REPLY OK received from other NodeB\n", ip_nodeb_, NOW);
					// update resources
					update_flow(rh->ack(), rh->frag(), ihp->flowid(), ihp->saddr(),0);
					send_res_reply(p->copy(),1);	// send the resource reply message
				} else {
					// reply failure
					if (verbose_) printf("Nodeb %d at %f RRC: rx RES_REPLY FAILURE received from other NodeB\n", ip_nodeb_, NOW);
					// remove resources from the RLC layer
					rlc_->remove_flow(ihp->flowid(),ihp->daddr());
					send_res_reply(p->copy(),0);	// send a failure
				}
			}

			// Release request message received from other cell
			if (lh->lltype() == LL_RELEASE_REQ) {
				if (verbose_) printf("Nodeb %d at %f RRC: LL_RELEASE_REQ arrived from other cell\n",
					ip_nodeb_, NOW);
				i = look_for(ihp->daddr());
				// fill physical and common headers
				ph->sa() = phy_->nodeb_address_;
				ph->da() = ue_info_[i].phyaddr_;
				ch->direction() = hdr_cmn::DOWN;
				ch->channel_t() = CCCH;	// ch allocation
				ch->next_hop() = ihp->daddr();
				s.schedule(downtarget_, p, delay_);  // send the packet down through the scheduler
				return;
			}

			// Release reply message received from other cell
			if (lh->lltype() == LL_RELEASE_REPLY) {
				if (verbose_) printf("Nodeb %d at %f RRC: LL_RELEASE_REPLY arrived from other cell\n",
					ip_nodeb_, NOW);
				int j = look_for(ihp->daddr());
				remove_flow_ul(p, j); // remove the uplink resources allocated for that flow
				ph->da() = ue_info_[j].phyaddr_;
				send_release_reply(p);	// send release reply message
				return;
			}

			// message notifing a handover received from the new node B
			if (lh->lltype() == LL_HANDOVER) {
				i = look_for(ihp->saddr());
				if (i != -1) {
					if (verbose_) printf("Nodeb %d at %f RRC: HANDOVER received from other NodeB to delete UE %d\n",
						ip_nodeb_, NOW,ue_info_[i].ipaddr_);
					if (ue_info_[i].dlsf_ > 0) {
						remove_res(i);	//remove from OSVF trees
						ue_info_[i].ipaddr_ = -1;
						//ph->sa() = ue_info_[i].phyaddr_;
						// deregister that user in the IFQ
						ifq_->reg(ihp->saddr(), 0.0);
						// start the timer for removing physical resources
						remove_->AddANode(ue_info_[i].phyaddr_);
						remove_->start(ue_info_[i].phyaddr_, 40 * UMTS_FrameTime);
						// send all the packets stored in the queue to the new node B
						ptr = flows_->GetNode(ihp->saddr());
						if (ptr != NULL) {
							while (ptr != NULL) {
								if (!ue_node(ptr->srcaddr_)) {
									// Delete the timer from the list of timers
									removeflow_->DeleteANode(ptr->srcaddr_, ptr->flowid_);
								}
								flows_->DeleteANode(ptr);
								ptr = flows_->GetNode(ihp->saddr());
							}
							temp = fl_->length();
							while (temp > 0) {
								Packet* prov;
								prov = fl_->deque();
								struct hdr_ip *ihprov = HDR_IP(prov);
								if ((ihprov->daddr() == ihp->saddr())){
									struct hdr_cmn *chprov = HDR_CMN(prov);
									chprov->channel_t() = DTCH;	// ch allocation
									chprov->direction() = hdr_cmn::UP;
									send_to_ra(prov->copy());	// send the packet to the routing agent
								}else
									fl_->enque(prov->copy());
								--temp;
								Packet::free(prov);
							}
						}
					}
					// update fields
					ue_info_[i].ipaddr_ = -1;
					ue_info_[i].phyaddr_ = -1;
					ue_info_[i].dltempsf_ = -1;
					ue_info_[i].dlsf_ = -1;
					ue_info_[i].dlfreq_ = -1;
					ue_info_[i].dltotalr_ = 0;
					Packet::free(p);
					p = NULL;
					return;
				}
			}

			if ((lh->lltype() == LL_FAILURE) && (ch->ptype() == PT_RRC_FAILURE)){
				// No resources in other NodeB to support the flow, remove resources
				int pos = look_for(ihp->daddr());
				if (pos >= 0) {
					if (verbose_) printf("Nodeb %d at %f RRC: FAILURE arrived for flow %d and UE %d\n",
						ip_nodeb_, NOW, ihp->flowid(), ihp->daddr());
					rlc_->remove_flow(ihp->flowid(),ihp->daddr());
					double rtemp = lh->tx_rate();
					remove_res(pos); // remove resources from OSVF trees
					lh->tx_rate() = ue_info_[pos].dltotalr_ - rtemp;
					ue_info_[pos].dltotalr_ = 0;
					ue_info_[pos].dlsf_ = 0;
					if (lh->tx_rate() > 0){
						// re-allocate resources for the other applications
						if (free_res(p)){
							cal_sf(p);
							ifq_->reg(ihp->daddr(), get_bytes(pos));	// update bytes in IFQ
						}
					} else
						// deregister that UE in IFQ
						ifq_->reg(ihp->daddr(), 0.0);
					// fill physical, ll and common headers
					lh->tx_rate() = rtemp;
					update_phy_bytes_slot(pos);	// update bytes in physical layer
					ch->channel_t() = CCCH;		// ch allocation
					ch->direction() = hdr_cmn::DOWN;
					ph->sa() = phy_->nodeb_address_;
					ph->da() = ue_info_[pos].phyaddr_;
					ch->next_hop() = ue_info_[pos].ipaddr_;
					s.schedule(downtarget_, p->copy(), delay_);  // send the packet down through the scheduler
				}
			}
			Packet::free(p);
			p = NULL;
			break;

		default:
			i = look_for(ihp->daddr());
			if (i != -1) {
				// Resource request message received from other cell
				if (lh->lltype() == LL_RES_REQ){
					if (free_res(p)) {
						// there are enough resources, register flow
						flows_->AddANode(ihp->saddr(), ihp->daddr(), ihp->flowid(), 1);
						if (verbose_) printf("Nodeb %d at %f RRC: LL_RES_REQ arrived from a UE in other cell\n",ip_nodeb_, NOW);
						pag_proc(p->copy(),i);	// begins paging procedure
						return;
					} else {
						// there are not enough resources
						send_res_reply(p,0);	// send a failure
					}
				} else {	// data packet received
					// pkt originated in other UE from different cell or in a fixed node
					if (!refused->refused_flow(ihp->flowid())){
						// flow not refused
						ptr = flows_->GetNode(ihp->saddr(), ihp->daddr(), ihp->flowid());
						if (ptr !=NULL) {
							if (verbose_) printf("Nodeb %d at %f RRC: packet with direction DOWN received from UE %d\n"
								,ip_nodeb_, NOW, ihp->saddr());
							// is not the first pkt rx
							if (!ue_node(ihp->saddr())) {
								// the source is a fixed node, start timer
								removeflow_->start(ihp->saddr(), ihp->flowid(), 1.0);
							}
							if (ptr->wait_ == 1) {
								// waiting for paging response
								fl_->enque(p);	// store packet until response
								return;
							} else {
								// resources already allocated
								send_msg(p,i);	// send packet
								return;
							}
						} else {
							// is the first pkt of that flow rx
							if (free_res(p)) {   // in the downlink
								if (verbose_) printf("Nodeb %d at %f RRC: first packet of flow %d with direction DOWN received from UE %d. Giving resources\n"
									,ip_nodeb_, NOW, ihp->flowid(), ihp->saddr());
								if (!ue_node(ihp->saddr())) {
									filltype(p);
									// Add a timer to the list of timers
									removeflow_->AddANode(ihp->saddr(), ihp->daddr(), ch->size(), ihp->flowid());
									// start timer
									removeflow_->start(ihp->saddr(), ihp->flowid(), 1.0);
								}
								pag_proc(p->copy(),i);	// begin paging procedure
								flows_->AddANode(ihp->saddr(), ihp->daddr(), ihp->flowid(), 1);
								fl_->enque(p);	// store packet until response
								return;
							} else {
								if (verbose_) printf("Nodeb %d at %f RRC: first packet DOWN of flow %d received from UE %d. No resources available\n"
									,ip_nodeb_, NOW, ihp->flowid(), ihp->saddr());
								refused->add(ihp->flowid());
								// send failure message
								if (ue_node(ihp->saddr())) {
									// fill ll, common and ip headers
									lh->lltype() = LL_FAILURE;
									ch->ptype() = PT_RRC_FAILURE;
									nsaddr_t temp = ihp->daddr();
									ihp->daddr() = ihp->saddr();
									ihp->saddr() = temp;
									ihp->ttl() = 200;
									ch->channel_t() = FROM_RRC;	// ch allocation
									s.schedule(uptarget_, p, delay_);  // send the packet up through the scheduler
									return;
								}
							}
						}
					}
				}
			}
			if (verbose_) printf("Nodeb %d at %f RRC: pkt dropped\n",ip_nodeb_, NOW);
			Packet::free(p);
			p = NULL;
			break;
	}
	}
	return;
}

// registers the new UE in its internal structure
int LLNodeb::register_ue(Packet* p)
{
	int i, j;

	hdr_cmn *ch = HDR_CMN(p);
	hdr_ip *ih = HDR_IP(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);

	for (i=0; i<MAX_NUM_UE; i++){
		if (ue_info_[i].phyaddr_ == -1){
			// register the new UE in array ue_info_[]
			ue_info_[i].phyaddr_ = ph->sa();
			ue_info_[i].ipaddr_ = ih->saddr();
			if (verbose_) printf("Nodeb %d at %f RRC: LL_SETUP received. Reistering UE %d\n"
				,ip_nodeb_, NOW, ue_info_[i].ipaddr_);
			for (j=0; j<MAX_NUM_FLOWS; j++){
				if (*ph->flows_[j][2] > 0) {
					// Handover: has flows already running
					// register flows in RLC layer
					update_flow(*ph->flows_[j][0],*ph->flows_[j][1],*ph->flows_[j][2], ih->saddr(),1);
				} else {
					break;
				}
			}
			return (1);
		}
	}
	return (0);
}

// gets bytes per tti for a specific user in IFQ
double LLNodeb::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

	if (ue_info_[pos].dlsf_ > 0) {
		bits = 2 * (2560 / ue_info_[pos].dlsf_) * (ifq_->tti() / UMTS_SlotTime);  // bits per tti
		sum = ue_info_[pos].dltotalr_;
		if (verbose_) printf("Nodeb %d at %f RRC: UE %d with DL sf %d and rate %f\n",
			ip_nodeb_,NOW,ue_info_[pos].ipaddr_,ue_info_[pos].dlsf_,ue_info_[pos].dltotalr_);
		if (sum > 0) {
			if (sum > 1920000)  // more than one dpdch is needed
				bits = bits * ((int)(sum / 1920000) + 1);
		}
		bits = bits/2;	// apply Convolutional Half Rate coding
		bits = (bits * rlcfsize) / (rlcfsize + rlchdrsize + machdrsize);
		if (verbose_) printf("Nodeb %d at %f RRC: ~~~~~~~~~~~~~~~ bytes_tti in IFQNodeB %f ~~~~~~~~~~~~~~\n",ip_nodeb_, NOW, bits / 8);
		return(bits/8);
	} else {
		return (0.0);
	}
}

// calculate and update bytes in physical layer
void LLNodeb::update_phy_bytes_slot(int pos)
{
	double bytes_slot;

	if (ue_info_[pos].dltotalr_ > 0) {
		// bytes per slot in physical layer for the actual SF for that UE
		bytes_slot = (2 * (2560 / (double)ue_info_[pos].dlsf_) / 8);
		if (ue_info_[pos].dltotalr_ > 1920000)
			// case of several DPDCHs
			bytes_slot = bytes_slot * ((int)(ue_info_[pos].dltotalr_ / 1920000) + 1);
	} else {
		bytes_slot = 0.0;	// none application for that UE
	}
	if (verbose_) printf("Nodeb %d at %f RRC: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bytes_slot in PHY %f for UE %d~~~~~~~~~~~~~~\n"
		,ip_nodeb_, NOW, bytes_slot, ue_info_[pos].phyaddr_);
	ip_nodeb_, NOW, phy_->slot_bytes(ue_info_[pos].phyaddr_, bytes_slot);	// pass the result to phy layer
	return;
}

// returns the code for applications originated in fixed nodes
int LLNodeb::getcode(nsaddr_t src, int flow)
{
	int code;	// 0 TURBO, 1 CONV_HALF, 2 CONV_THIRD
	Node * node_ = (Node*)(phy_->netif_->node());
	// get the type of channel coding for the flow
	int type = node_->list->lookfortype(getip(src), flow);
	switch (type) {
		case 0:	// audio
			code = 0;
			break;
		case 1:	// video
			code = 0;
			break;
		case 2:	// mail
			code = 2;
			break;
		case 3:	// fax
			code = 1;
			break;
		case 4:	// speech
			code = 1;
			break;
		case 5:	// ftp
			code = 0;
			break;
		case 6:	// http
			code = 1;
			break;
		default:
			code = -1;
			break;
	}
	return code;
}

// calculates the spreading factor for a given packet
int LLNodeb::cal_sf(Packet* p)
{
	hdr_ll *lh = HDR_LL(p);
	hdr_ip *ih = HDR_IP(p);
	hdr_cmn *ch = HDR_CMN(p);
	double index; 	// bits per slot

	if (!ue_node(ih->saddr())) {
		// if the source is a fixed node takes the inf from list
		Node * node_ = (Node*)(phy_->netif_->node());
		index = node_->list->lookforrate(getip(ih->saddr()), ih->flowid());
		int code = getcode(ih->saddr(), ih->flowid());	// take channel coding
		index = phy_rate(index, code, ch->size());	// get physical rate for the application
										// with the channel coding already calc.
		index = index * UMTS_SlotTime / 8; // bytes per slot in physical layer
	} else {
		// takes the information from the packet
		index = lh->tx_rate() * UMTS_SlotTime / 8; // bytes per slot in physical layer
	}

	if (index > 0) { // calculate the SF for that physical rate
		index = (2*320 / index);
		index = (int)(log(index)/log(2));
		int i, r, sf_ = 2;
		if (index > 9) index = 9;
		if (index < 2) index = 2;
		for (i=0; i<((int)index - 1); i++) {
			sf_ *= 2;
		}
		if (verbose_) printf("Nodeb %d at %f RRC: the DL SF given is %d\n", ip_nodeb_, NOW,sf_);
		return(sf_);
	} else
		return(0);
}

// calculates the spreading factor for a given physical rate
int LLNodeb::cal_sf(double rate)
{
	double index; 	// bits per slot

	index = rate * UMTS_SlotTime / 8; // bytes per slot in physical layer
	if (index > 0) { // calculate the SF for that physical rate
		index = (2*320 / index);
		index = (int)(log(index)/log(2));
		int i, r, sf_ = 2;
		if (index > 9) index = 9;
		if (index < 2) index = 2;
		for (i=0; i<((int)index - 1); i++) {
			sf_ *= 2;
		}
		if (verbose_) printf("Nodeb %d at %f RRC: the DL SF given is %d\n", ip_nodeb_, NOW,sf_);
		return(sf_);
	} else
		return(0);
}

// allocate resources in the OSVF trees
int LLNodeb::alloc_res(Packet* p, int sf)
{
	hdr_ip *ih = HDR_IP(p);
	int i, r;
	if (verbose_) printf("\nNodeb %d at %f RRC: Trying to give sf %d from OVSF trees\n", ip_nodeb_, NOW,sf);
	int j = Random::integer(MAX_NUM_FREQ-1);
	for (i=0; i<MAX_NUM_FREQ; i++) {	// for any frequencies
		if (dl_sftree[j]->insert_sf(getip(ih->daddr()), sf)) {// allocate resources
			r = look_for(ih->daddr());
			ue_info_[r].dlsf_ = sf;		// update SF for that UE
			ue_info_[r].dlfreq_ = j + 12; // update freq for that UE (downlink)
			return(1);
		}
		j++;
		if (j == MAX_NUM_FREQ) j = 0;
	}

	return(0);
}

// remove resources in the OSVF trees
int LLNodeb::remove_res(int pos)
{
	int i;
	if (verbose_) printf("Nodeb %d at %f RRC: Removing UE %d with sf %d and freq %d from OSVF\n",
		ip_nodeb_, NOW,ue_info_[pos].ipaddr_, ue_info_[pos].dlsf_,ue_info_[pos].dlfreq_ - 12);
	if (dl_sftree[ue_info_[pos].dlfreq_ - 12]->remove_sf(ue_info_[pos].ipaddr_, ue_info_[pos].dlsf_)) {
		// resources removed successfully
		return(1);
	}
	// resuorces not removed successfully
	return(0);
}

// remove downlink resources allocated for that flow
void LLNodeb::remove_flow_dl(Packet* p)
{
	hdr_ip *ihp = HDR_IP(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ll *lh = HDR_LL(p);

	int temp;
	int i = look_for(ihp->saddr());
	if (verbose_) printf("Nodeb %d at %f RRC: remove_flow_dl of UE with ip %d total rate stored %e rate in the message %e\n",
		ip_nodeb_, NOW, ihp->saddr(),ue_info_[i].dltotalr_,lh->tx_rate());
	if (ue_info_[i].dlsf_ > 0) {	// flows already allocated for that node
		remove_res(i);	//remove downlink resources from OSVF trees
		rlc_->remove_flow(ihp->flowid(),ue_info_[i].ipaddr_);	// remove resources in RLC layer
		ue_info_[i].dltotalr_ -= lh->tx_rate();	// decrease physical rate for that UE
		if (ue_info_[i].dltotalr_ > 0) {
			// there are still active flows for that UE; reallocate resources
			if (verbose_) printf("Nodeb %d at %f RRC: remove_flow_dl: given resources again\n", ip_nodeb_, NOW);
			int sf = cal_sf(ue_info_[i].dltotalr_);	// recalculate the needed SF
			nsaddr_t tempd = ihp->daddr();
			ihp->daddr() = ihp->saddr();
			alloc_res(p->copy(),sf);	// reallocate resources in OSVF trees
			ihp->daddr() = tempd;
		} else {
			// that UE has not got any other application running
			ue_info_[i].dltotalr_ = 0;
			ue_info_[i].dlsf_ = -1;
		}
		ph->sa() = ue_info_[i].phyaddr_;
		ifq_->reg(ihp->saddr(), get_bytes(i));	// update IFQ layer for that UE
		update_phy_bytes_slot(i);		// update physical rate needed for that UE
		FListPtr ptr = flows_->GetNode(ihp->daddr(), ihp->saddr(), ihp->flowid());
		if (ptr != NULL) {
			// delete flow from the list
			flows_->DeleteANode(ptr);
			temp = fl_->length();
			while (temp > 0) {	// free packets of that flow waiting for resource allocation
				Packet* prov;
				prov = fl_->deque();
				struct hdr_ip *ihprov = HDR_IP(prov);
				if ((ihprov->flowid() == ihp->flowid()) && (ihprov->daddr() == ihp->saddr())){
					drop(prov);
				}else
					fl_->enque(prov);
				--temp;
			}
		}
	}
	return;
}

// remove the uplink resources allocated for that flow
void LLNodeb::remove_flow_ul(Packet* p, int pos)
{
	hdr_ip *ihp = HDR_IP(p);
	if (verbose_) printf("Nodeb %d at %f RRC: Removing UL resources to UE %d\n"
		,ip_nodeb_, NOW,ue_info_[pos].ipaddr_);
	rlc_->remove_flow(ihp->flowid(),ue_info_[pos].ipaddr_);	// remove resources in RLC layer
	if (ue_info_[pos].ipaddr_ == ihp->saddr()){	// update addresses
		ihp->saddr() = ihp->daddr();
		ihp->daddr() = ue_info_[pos].ipaddr_;
	}
	return;
}

// update the static structure registry_
void LLNodeb::update_db(Packet* p)
{
	int i, j;

	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ip *ih = HDR_IP(p);

	// UE already registered (handover procedure)
	for (i=0; i<MAX_NUM_NODES; i++){
		if (registry_[i].ue_ == ih->saddr()){
			update_nodeb(p->copy(), registry_[i].nodeb_);// send a HANDOVER message to the former Nodeb
			registry_[i].nodeb_ = ip_nodeb_;	// update the registry_
			if (verbose_) printf("Nodeb %d at %f RRC: Handover procedure, ue(ip): %d updated\n"
				,ip_nodeb_, NOW, getip(ih->saddr()));
			break;
		}
	}

	// UE not registered in the system (turn on procedure)
	if (i == MAX_NUM_NODES) {
		for (i=0; i<MAX_NUM_NODES; i++){
			if (registry_[i].ue_ < 0){		// update registry_
				registry_[i].ue_ = ih->saddr();
				registry_[i].nodeb_ = ip_nodeb_;
				if (verbose_) printf("Nodeb %d at %f RRC: Turn on procedure, ue(ip): %d updated\n",
					ip_nodeb_, NOW,  getip(ih->saddr()));
				break;
			}
		}
	}
	return;
}

// send a HANDOVER message to the former Nodeb for the incoming UE
void LLNodeb::update_nodeb(Packet* p, nsaddr_t addr)
{
	hdr_ip *ih = HDR_IP(p);
	hdr_ll *lh = HDR_LL(p);
	hdr_cmn *ch = HDR_CMN(p);

	// fill common and ip headers
	ch->direction() = hdr_cmn::DOWN;
	ch->channel_t() = FROM_RRC;	// ch allocation
	ch->next_hop() = addr;
	ih->daddr() = addr;
	lh->lltype() = LL_HANDOVER;	// set type of message
	send_to_ra(p);	// send the packet to the routing agent
	return;
}

// returns the position in ue_info_[] fo that UE
int LLNodeb::look_for(nsaddr_t ad)
{
	int i;
	for (i=0; i<MAX_NUM_UE; i++) {
		if (ue_info_[i].ipaddr_ == ad)
			return(i);	// found it
	}
	return(-1);	// not found it
}

// returns if the UE is a fixed node (0) or a UE (1)
int LLNodeb::ue_node(nsaddr_t source)
{
	int i;
	int dir = getip(source);
	for (i=0; i<MAX_NUM_NODES; i++){
		if (registry_[i].ue_ == source){	// UE
			return(1);
		}
	}
	return(0);	// fixed node
}

// send data packets downwards
void LLNodeb::send_msg(Packet* p, int loc)
{
	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 physical, ll, rlc, common and ip headers
	lhp->lltype() = LL_DATA;

	php->sa() = phy_->nodeb_address_;
	php->da() = ue_info_[loc].phyaddr_;
	php->freq() = ue_info_[loc].dlfreq_;

	chp->channel_t() = DTCH;	// ch allocation
	chp->next_hop_ = ihp->daddr();
	chp->prev_hop_ = ip_nodeb_;
	chp->direction() = hdr_cmn::DOWN;
	chp->error() = 0;

	// if it is a flow generated in a wired node, rlc in frag mode (default)
	if (!ue_node(ihp->saddr())) {
		hdr_rlc_umts *rhp = HDR_RLC_UMTS(p);
		rhp->frag() = 1;
		rhp->bopno() = 0;
		rhp->rlctype() = RLC_DATA;
		i = getcode(ihp->saddr(), ihp->flowid());	// get channel coding
		switch (i){
			case 0:	// TURBO
				php->c_coding() = TURBO;
				break;
			case 1:	// CONV_HALF
				php->c_coding() = CONV_HALF;
				break;
			case 2:	// CONV_THIRD
				php->c_coding() = CONV_THIRD;
				break;
		}

	}

	if (verbose_) printf("Nodeb %d at %f RRC: sending LL_DATA to UE: %d\n", ip_nodeb_, NOW,php->da());
	s.schedule(downtarget_, p, delay_);  // send the packet down through the scheduler

	return;
}

// send SETUP REPLY message for setup procedure and handover
void LLNodeb::send_setup_reply(Packet* p)
{
	Scheduler& s = Scheduler::instance();
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	struct hdr_ip *ih = HDR_IP(p);
	struct hdr_ll *lh = HDR_LL(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 physical, ll, common and ip headers
	lhsp->lltype() = LL_SETUP_REPLY;
	lhsp->tx_rate() = lh->tx_rate();	// physical rate requested

	phsp->sa() = phy_->nodeb_address_;
	phsp->da() = ph->sa();
 	phsp->sf() = 256;
	phsp->c_coding() = CONV_HALF;
	phsp->freq() = Random::integer(MAX_NUM_FREQ -1);	// randomly chosen frequency

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

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

	if (verbose_) printf("Nodeb %d at %f RRC: sending LL_SETUP_REPLY to UE %d\n",ip_nodeb_, NOW, ih->saddr());
	s.schedule(downtarget_, sp->copy(), delay_);  // send the packet down through the scheduler

	// for registering in classifiers and routing table
	s.schedule(uptarget_, sp, delay_);  // send the packet up through the scheduler

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

	return;
}

// send the resource reply message or a failure
// must be checked if the response has to be for a ue in our cell or in other cell.
void LLNodeb::send_res_reply(Packet* p, int state)
{
	int i;
	Scheduler& s = Scheduler::instance();
	hdr_phy *ph = HDR_PHY_UMTS(p);
	struct hdr_ip *ih = HDR_IP(p);
	struct hdr_ll *lh = HDR_LL(p);

	Packet* sp = p->copy();
	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 and common headers
	lhsp->lltype() = LL_RES_REPLY;

	phsp->sa() = phy_->nodeb_address_;
	phsp->sf() = 256;
	phsp->c_coding() = CONV_HALF;
	phsp->freq() = Random::integer(MAX_NUM_FREQ -1);	// randomly chosen frequency

	i = look_for(ih->saddr());	// fill the destination address (physical)
	if (i != -1) {
		phsp->da() = ue_info_[i].phyaddr_;
	}

	chsp->channel_t() = CCCH;  // ch allocation
	chsp->direction() = hdr_cmn::DOWN;
	chsp->size() = 5;
	chsp->error() = 0;
	if (state == 0)	// if was a FAILURE, fill the packet type properly
		chsp->ptype() = PT_RRC_FAILURE;

	if (look_for(ihsp->saddr()) >= 0) {	// the requesting party is in our cell
		if (verbose_) printf("Nodeb %d at %f RRC: sending LL_RES_REPLY from ip: %d to ip: %d\n",
			ip_nodeb_, NOW, phsp->sa(), getip(ihsp->saddr()), getip(ihsp->daddr()));
		chsp->next_hop_ = ih->saddr();
		s.schedule(downtarget_, sp, delay_);  // send the packet down through the scheduler
	} else {	// it is in another cell
		chsp->ptype() = PT_CBR;
		chsp->channel_t() = FROM_RRC;  // ch allocation
		ihsp->saddr() = ih->daddr();
		ihsp->daddr() = ih->saddr();
		ihsp->ttl() = 200;
		if (verbose_) printf("Nodeb %d at %f RRC: sending LL_RES_REPLY to other cell from ip: %d to ip: %d\n",
			ip_nodeb_, NOW, phsp->sa(), getip(ihsp->saddr()), getip(ihsp->daddr()));
		s.schedule(uptarget_, sp, delay_);  // send the packet up through the scheduler
	}

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

	return;
}

// send paging message to the destination of the incoming flow
void LLNodeb::pag_proc(Packet* p, int pos)
{
	int i, j;
	Scheduler& s = Scheduler::instance();
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ll *lh = HDR_LL(p);
	hdr_rlc_umts *rh = HDR_RLC_UMTS(p);
	struct hdr_ip *ih = HDR_IP(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);
	struct hdr_rlc_umts *rhsp = HDR_RLC_UMTS(sp);

	// fill physical, ll, rlc, common and ip headers
	lhsp->lltype() = LL_PAGING;
	lhsp->tx_rate() = lh->tx_rate();

	phsp->sa() = phy_->nodeb_address_;
	phsp->da() = ue_info_[pos].phyaddr_;
	phsp->freq() = ue_info_[pos].dlfreq_;

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

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

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

	if (verbose_) printf("Nodeb %d at %f RRC: initiating PAGING PROCEDURE to UE %d\n",ip_nodeb_, NOW, phsp->da());
	s.schedule(downtarget_, sp, delay_);  // send the packet down through the scheduler

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

	return;
}

// send the packet to the routing agent
void LLNodeb::send_to_ra(Packet* p)
{
	Scheduler& s = Scheduler::instance();
	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_ip *ih = HDR_IP(p);
	ch->ptype() = PT_CBR;
	ih->ttl() = 200;
	if (verbose_) printf("Nodeb %d at %f RRC: pkt for ue(ip): %d which is in other cell. ttl %d delay %f\n",
		ip_nodeb_, NOW, getip(ih->daddr()), ih->ttl(), delay_);
	s.schedule(uptarget_, p, delay_);   // send the packet up through the scheduler
	return;
}

// returns the maximum rate allowed for the SF
double get_rate(int sf)
{
	return((2*2560 / sf) / UMTS_SlotTime);
}

// returns the physical rate corresponding for the user rate, channel coding and packet size passed
double LLNodeb::phy_rate(double rate, int 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);	// n of rlc packet 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
	switch (code) {	// adjust rate depending of the channel coding
		case 0:	// TURBO
   			rt = 3*rt;
			break;
		case 1:	// CONV_HALF
   			rt = 2*rt;
			break;
		case 2:	// CONV_THIRD
   			rt = 3*rt;
			break;
		default:
			break;
	}
	if (verbose_) printf("Nodeb %d at %f RRC: ********************* rate %f for code %d\n"
		,ip_nodeb_, NOW, rt, code);
	return(rt);
}

// allocate downlink resources in this layer and OSVF trees if there are enough available (returns 1)
// and returns 0 if not.
int LLNodeb::free_res(Packet* p)
{
	int i, sf, pos, code;
	double temp_rate, user_rate;
	struct hdr_ll *lh = HDR_LL(p);
	struct hdr_ip *ih = HDR_IP(p);
	struct hdr_phy *ph = HDR_PHY_UMTS(p);
	struct hdr_cmn *ch = HDR_CMN(p);

	pos = look_for(ih->daddr());
	if (!ue_node(ih->saddr())) {	// the originating party is a fixed node
		Node * node_ = (Node*)(phy_->netif_->node());
		user_rate = node_->list->lookforrate(getip(ih->saddr()), ih->flowid()); // get user rate
		code = getcode(ih->saddr(), ih->flowid());	// get channel coding
		temp_rate = phy_rate(user_rate, code, ch->size());	// get physical rate needed for the flow
	} else {
		temp_rate = lh->tx_rate();	// is a UE, take the physical rate needed from the LL header
	}
	if (ue_info_[pos].dlsf_ > 0){ // flows already allocated for that node
		double tr = get_rate(ue_info_[pos].dlsf_);	// get maximum physical rate allowed for the
										// allocated SF for that UE in downlink
		if (tr >= (ue_info_[pos].dltotalr_ + temp_rate)) {	// is able to support new flow
			if (verbose_) printf("Nodeb %d at %f RRC: UE %d stored rate %f and new rate arrived %f\n",
				ip_nodeb_, NOW, ue_info_[pos].ipaddr_,ue_info_[pos].dltotalr_,temp_rate);
			ue_info_[pos].dltotalr_ += temp_rate;	// update registers
			if (verbose_) printf("Nodeb %d at %f RRC: UE %d total rate stored %f\n",
				ip_nodeb_, NOW, ue_info_[pos].ipaddr_, ue_info_[pos].dltotalr_);
			return(1);	// OK
		} else {	// is not able to support new flow, new resources has to be allocated
			ue_info_[pos].dltempsf_ = ue_info_[pos].dlsf_;
			remove_res(pos);	// remove resources from OSVF trees
			sf = cal_sf(temp_rate + ue_info_[pos].dltotalr_);	// calculate needed SF
			if (alloc_res(p,sf)) {	// allocate resources in OSVF downlink trees
				if (verbose_) printf("Nodeb %d at %f RRC: UE %d stored rate %f and new rate arrived %f\n",
					ip_nodeb_, NOW, ue_info_[pos].ipaddr_, ue_info_[pos].dltotalr_,temp_rate);
				ue_info_[pos].dltotalr_ += temp_rate;	// update registers
				if (verbose_) printf("Nodeb %d at %f RRC: UE %d total rate stored %f\n",
					ip_nodeb_, NOW, ue_info_[pos].dltotalr_);
				return(1);	// OK
			} else { // not possible to allocate resuorces in OSVF trees
				ue_info_[pos].dlsf_ = ue_info_[pos].dltempsf_;
				ue_info_[pos].dltempsf_ = -1;
				alloc_res(p,ue_info_[pos].dlsf_);	// give the previous resources
				return(0);	// not possible
			}
		}
	} else {	// no flows already allocated for that node
		sf = cal_sf(p);	// get the needed SF for the flow
		ue_info_[pos].dltempsf_ = -1;
		if (alloc_res(p,sf)) {	// allocate resources in OSVF trees
			if (verbose_) printf("Nodeb %d at %f RRC: UE %d stored rate %f and new rate arrived %f\n",
					ip_nodeb_, NOW, ue_info_[pos].ipaddr_, ue_info_[pos].dltotalr_,temp_rate);
			ue_info_[pos].dltotalr_ += temp_rate;	// update resgisters
			if (verbose_) printf("Nodeb %d at %f RRC: UE %d total rate stored %f\n",
					ip_nodeb_, NOW, ue_info_[pos].ipaddr_, ue_info_[pos].dltotalr_);
			return(1);	// OK
		}
	}
	return (0);	// not possible
}

// fill rlc header fields: frag_ and ack_ depending on the flow
void LLNodeb::filltype(Packet* p)
{
	hdr_ip *ih = HDR_IP(p);
	hdr_rlc_umts *rh = HDR_RLC_UMTS(p);

	int acked, fraged;
	Node * node_ = (Node*)(phy_->netif_->node());	// get node
	int type = node_->list->lookfortype(getip(ih->saddr()), ih->flowid());	// get type of application

	switch (type) {
	case 0:	// audio
		fraged = 1;
		acked = 0;
		break;
	case 1:	// video
		fraged = 1;
		acked = 0;
		break;
	case 2:	// mail
		fraged = 1;
		acked = 1;
		break;
	case 3:	// fax
		fraged = 1;
		acked = 0;
		break;
	case 4:	// speech
		fraged = 1;
		acked = 0;
		break;
	case 5:	// ftp
		fraged = 1;
		acked = 1;
		break;
	case 6:	// http
		fraged = 1;
		acked = 1;
		break;
	default:
		fraged = -1;
		acked = -1;
		break;
	}

	rh->frag() = fraged;	// assign value
	rh->ack() = acked;	// assign value
	return;
}

// give resources in RLC layer for the flow
void LLNodeb::update_flow(int acked, int fraged, int flowid, nsaddr_t src, int handover)
{
	if (verbose_) printf("Nodeb %d at %f RRC: Updating flow %d of source %d Nodeb: ack, frag [%d %d]\n"
		,ip_nodeb_, NOW, flowid, src, acked, fraged);
	rlc_->store_flow(acked, fraged, flowid, src, handover);	// update RLC layer
	return;
}

// send release reply message
void LLNodeb::send_release_reply(Packet* p)
{
	Scheduler& s = Scheduler::instance();
	hdr_phy *ph = HDR_PHY_UMTS(p);
	struct hdr_ip *ih = HDR_IP(p);
	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_ll *lh = HDR_LL(p);

	// fill physical, ll and common headers
	lh->lltype() = LL_RELEASE_REPLY;
	ph->sa() = phy_->nodeb_address_;
	ch->channel_t() = CCCH;  // ch allocation
	ch->direction() = hdr_cmn::DOWN;

	if (verbose_) printf("Nodeb %d at %f RRC: sending RELEASE REPLY from ip: %d to ip: %d with da phy %d\n",
		ip_nodeb_, NOW, getip(ih->saddr()), getip(ih->daddr()), ph->da());
	ch->next_hop_ = ih->daddr();
	s.schedule(downtarget_, p, delay_);  // send the packet down through the scheduler

	return;
}

// handler for ReleaseUETimer, release resources in physical layer and mac layer
void LLNodeb::RemoveUEHandler(int addr)
{
	if (verbose_) printf("\nNodeb %d at %f RRC: REMOVING RESOURCES OF Source %d+++++++++++++++++++++++++++++++++++++\n\n"
		,ip_nodeb_, NOW, addr);
	phy_->slot_bytes(addr, 0);	// slot bytes = 0 in physical layer
	phy_->remove_ue(addr);		// remove UE in physical layer
	mac_->remove(addr);	// remove resources in MAC layer
	remove_->DeleteANode(addr);
	return;
}

// handler for AppsNodebTimer (contained in TNodebList), send a Connection release message
void LLNodeb::RemoveFlowHandler(nsaddr_t src, nsaddr_t dest, int size, int flow)
{
	// The application has finished, a Connection release has to be sent
	Scheduler& s = Scheduler::instance();
	int pos = look_for(dest);

	Packet* sp = Packet::alloc();	// contruct the packet to send
	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

	Node * node_ = (Node*)(phy_->netif_->node());
	double user_rate = node_->list->lookforrate(getip(src), flow);	// get user rate
	int code = getcode(src, flow);	// get channel coding
	lhsp->tx_rate() = phy_rate(user_rate, code, size);	// fill physical rate
	lhsp->lltype() = LL_RELEASE_REQ;	// fill type of message

	phsp->sa() = phy_->nodeb_address_;
	phsp->da() = ue_info_[pos].phyaddr_;
	phsp->c_coding() = CONV_HALF;

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

	ihsp->flowid() = flow;
	ihsp->saddr() = src;
	ihsp->daddr() = dest;

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

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

	return;
}


