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

/* Modified and extended 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.
*/

/* By Sandeep Kumar, Kopparapu Suman and Richa Jain,
 * Indian Institute of Technology, Bombay.
 * June, 2001.
*/

/* Copyright (c) 2001 Indian Insitute of Technology, Bombay.
 * 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 Indian Insitute of
 * Technology, Bombay.
 *
 * 3. The name of the Institute may not be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 * INDIAN INSTITUTE OF TECHNOLOGY, BOMBAY, 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 "connector.h"
#include "packet.h"
#include "random.h"
#include "mobilenode.h"
#include "address.h"

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

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

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

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


	rlc->rTxHandler(flow_);
}

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

rTxList:: ~rTxList()
{ rTxTimerPtr temp = Head;
  CurrentPtr = Head;

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

void rTxList::AddANode(int flow)
{Tail->Next = new rTxTimer(rlc);
 Tail=Tail->Next;
 Tail->flow_ = flow;
 return;
}

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

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

void rTxList::DeleteANode(rTxTimerPtr corpse)
{ rTxTimerPtr 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 rTxList::DeleteANode(int flow)
{ rTxTimerPtr 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
}

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

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

void rTxList::stop(int flow)
{ rTxTimerPtr corpse;
  corpse = GetNode(flow);
  corpse->stop();
  return;
}

/****************************** RlcUmts **********************************/

int hdr_rlc_umts::offset_;

static class RlcUmtsHeaderClass : public PacketHeaderClass {
public:
	RlcUmtsHeaderClass(): PacketHeaderClass("PacketHeader/RlcUmts",
					    sizeof(hdr_rlc_umts)) {
		bind_offset(&hdr_rlc_umts::offset_);
	}
} class_hdr_rlc_umts;

static class RlcUmtsClass : public TclClass {
public:
	RlcUmtsClass() : TclClass("Rlc/Umts") {}
	TclObject* create(int, const char*const*) {
		return (new RlcUmts);
	}
} class_rlc_umts;

int RlcUmts::rlcverbose_ = {0};

RlcUmts::RlcUmts() : LinkDelay(), downtarget_(0), uptarget_(0)

{
	bind_time("rlctime_",&rlctime_);
	bind("rlcverbose_",&rlcverbose_);
	bind("rlcfragsz_",&rlcfragsz_);

	length_ = 0;
	handover_ = 0;
	buf_= new PacketQueue();
	int i;
	for (i=0; i<MAX_FLOWS; i++){
		info_[i].Txbuf_ = new PacketQueue();
		info_[i].Rxbuf_ = new PacketQueue();
		info_[i].flow_ = -1;
		info_[i].fraged_ = 1;
		info_[i].acked_ = 0;
		info_[i].seqno_ = 0;				// link-layer sequence number
		info_[i].ackno_ = 0;				// ACK received so far
		info_[i].rackno_ = -1;			// seq no of left most pkt
		info_[i].window_ = 500;			// window size for sack
		info_[i].unackseqno_ = 0;
		info_[i].numdups_ = 0;
	}
	for (i=0; i<MAX_FLOWS; i++){
		hinfo_[i].Txbuf_ = new PacketQueue();
		hinfo_[i].Rxbuf_ = new PacketQueue();
		hinfo_[i].flow_ = -1;
		hinfo_[i].fraged_ = 1;
		hinfo_[i].acked_ = 0;
		hinfo_[i].seqno_ = 0;				// link-layer sequence number
		hinfo_[i].ackno_ = 0;				// ACK received so far
		hinfo_[i].rackno_ = -1;			// seq no of left most pkt
		hinfo_[i].window_ = 500;			// window size for sack
		hinfo_[i].unackseqno_ = 0;
		hinfo_[i].numdups_ = 0;
	}
	rtx_ = new rTxList(this);
}


int RlcUmts::command(int argc, const char*const* argv)
{
	Tcl& tcl = Tcl::instance();
	if (argc == 3) {
		if (strcmp(argv[1], "down-target") == 0) {
			downtarget_ = (NsObject*) TclObject::lookup(argv[2]);
			return (TCL_OK);
		}
		if (strcmp(argv[1], "up-target") == 0) {
			uptarget_ = (NsObject*) TclObject::lookup(argv[2]);
			return (TCL_OK);
		}
	}
	else if (argc == 2) {
		if (strcmp(argv[1], "getlen") == 0) {
			tcl.resultf("%d", info_[0].Txbuf_->length());
			return (TCL_OK);
		}
		if (strcmp(argv[1], "down-target") == 0) {
			tcl.resultf("%s", downtarget_->name());
			return (TCL_OK);
		}
		if (strcmp(argv[1], "up-target") == 0) {
			tcl.resultf("%s", uptarget_->name());
			return (TCL_OK);
		}
	}
	return LinkDelay::command(argc, argv);
}

int RlcUmts::look_for(int flow)
{
	int i;
	for (i=0; i<MAX_FLOWS; i++) {
		if (info_[i].flow_ == flow) {
			return(i);
		}
	}
	return(-1);
}

int RlcUmts::get_acked(int flow)
{
	int pos = look_for(flow);
	return(info_[pos].acked_);
}

int RlcUmts::get_fraged(int flow)
{
	int pos = look_for(flow);
	return(info_[pos].fraged_);
}

void RlcUmts::remove_flow(int flowid)
{
	int i;
	for (i=0; i<MAX_FLOWS; i++) {
		if (info_[i].flow_ == flowid) {
			info_[i].flow_ = -1;
			info_[i].fraged_ = 1;
			info_[i].acked_ = 0;
			rtx_->DeleteANode(flowid);
			if (rlcverbose_)
				printf("UE %d at %f RLC: //////////////////////////////////// REMOVE FLOW %d\n\n", ip_ue_, NOW, flowid);
			return;
		}
	}
}

void RlcUmts::store_flow(int acked, int fraged, int flowid)
{
	int i;
	for (i=0; i<MAX_FLOWS; i++) {
		if (info_[i].flow_ == flowid){
			info_[i].rackno_ = -1;
			info_[i].unackseqno_ = 0;
			return;
		}
	}

	for (i=0; i<MAX_FLOWS; i++) {
		if (info_[i].flow_ < 0){
			info_[i].flow_ = flowid;
			info_[i].acked_ = acked;
			info_[i].fraged_ = fraged;
			if (rlcverbose_)
				printf("UE %d at %f RLC: ///////////////////////////////// STORE FLOW %d\n\n",ip_ue_, NOW, flowid);
			rtx_->AddANode(flowid);

			return;
		}
	}
	return;
}

void RlcUmts::handover(int flag)
{
	int i;
	handover_ = flag;
	if (flag == 1) {
		for (i=0; i<MAX_FLOWS; i++) {
			hinfo_[i].flow_ = info_[i].flow_;
			hinfo_[i].fraged_ = info_[i].fraged_;
			hinfo_[i].acked_ = info_[i].acked_;
			hinfo_[i].seqno_ = info_[i].seqno_;
			hinfo_[i].ackno_ = info_[i].ackno_;
			hinfo_[i].rackno_ = info_[i].rackno_;
			hinfo_[i].window_ = 200;
			hinfo_[i].unackseqno_ = info_[i].unackseqno_;
			if (rlcverbose_)
				printf("UE %d at %f RLC: HANDOVER!!!!!!!!!!!!!!!!!!! hinfo_[i].unackseqno_ %d info_[i].unackseqno_ %d\n",ip_ue_, NOW, hinfo_[i].unackseqno_,info_[i].unackseqno_);
			hinfo_[i].numdups_ = info_[i].numdups_;
		}
	} else {
		for (i=0; i<MAX_FLOWS; i++){
			hinfo_[i].flow_ = -1;
			hinfo_[i].fraged_ = 1;
			hinfo_[i].acked_ = 0;
			hinfo_[i].seqno_ = 0;
			hinfo_[i].ackno_ = 0;
			hinfo_[i].rackno_ = -1;
			hinfo_[i].window_ = 500;
			hinfo_[i].unackseqno_ = 0;
			hinfo_[i].numdups_ = 0;
		}
	}
	return;
}


void RlcUmts::recv(Packet* p, Handler*)
{
	hdr_cmn *ch = HDR_CMN(p);
	hdr_rlc_umts *rlch = HDR_RLC_UMTS(p);
	hdr_ip *iph =HDR_IP(p);
	hdr_ll *lh = HDR_LL(p);

//	if (rlcverbose_==1) fprintf(stderr,"<%d> %f RLC: recv pkt_type=%s uid=%d ipsrc=%d ipdst=%d\n",
//			ip_ue_, NOW, packet_info.name(ch->ptype()), ch->uid_,
//			Address::instance().get_nodeaddr(iph->saddr()),
//			Address::instance().get_nodeaddr(iph->daddr()));

	if(ch->ptype() == PT_ACK){
		recvACK(p);
		return;
	} else {
		if (ch->channel_t() == DTCH) {
			//if ack pkt or data pkt...
			recvDATA(p);
		} else {
			Scheduler& s = Scheduler::instance();
			if (ch->direction() == hdr_cmn::UP) {
				ch->size() -= RLC_HDR_SZ;
				s.schedule(uptarget_, p, delay_);
			}

			if (ch->direction() == hdr_cmn::DOWN) {
				if (lh->lltype() == LL_RES_REQ) {
					int i = look_for(iph->flowid());
					rlch->ack() = info_[i].acked_;
					rlch->frag() = info_[i].fraged_;
				}
				ch->size() += RLC_HDR_SZ;
				s.schedule(downtarget_, p, delay_);
			}
		}
	}
	return;
}

void RlcUmts::recvDATA(Packet* p)
{
	int i;
	hdr_cmn *ch = HDR_CMN(p);
	hdr_ip *ih = HDR_IP(p);
	hdr_rlc_umts *rh = HDR_RLC_UMTS(p);

	i = look_for(ih->flowid());

	Scheduler& s = Scheduler::instance();

	if (ch->direction() == hdr_cmn::UP){
		if (rlcverbose_==1) fprintf(stderr,"<%d> %f RLC: DATA on its way up. call sendupDATA\n", ip_ue_, NOW);
		if ((info_[i].fraged_) || (info_[i].acked_)){
			ch->size() -= RLC_HDR_SZ;
			sendUpDATA(p, i);
		} else {
			ch->size() -= RLC_HDR_SZ;
			s.schedule(uptarget_, p, delay_);
		}
	}else{
		ch->direction() = hdr_cmn::DOWN;
		if (rlcverbose_==1) fprintf(stderr,"<%d> %f RLC: DATA going down. call enqueDATA\n", ip_ue_, NOW);
		if ((info_[i].fraged_) || (info_[i].acked_)){
			enqueDATA(p, i);
		} else {
			ch->size() += RLC_HDR_SZ;
			s.schedule(downtarget_, p, delay_);
		}
	}
}


void RlcUmts::enqueDATA(Packet* p, int pos)
{
	hdr_cmn *ch = HDR_CMN(p);
	hdr_ip *ih = HDR_IP(p);
	hdr_rlc_umts *rlch = HDR_RLC_UMTS(p);

	rlch->rlctype() = RLC_DATA;

	//fragmenting to start here....
	int psize, mod;
	if(info_[pos].fraged_ == 1)
		psize = rlcfragsz_;
	else
		psize = ch->size_;

	int bopno = info_[pos].seqno_;
	int eopno = bopno + (int)((ch->size_-1)/psize);

	int i;

	for ( i = bopno; i <= eopno; i++){
		Packet * sp = p->copy();
		hdr_cmn *chsp = HDR_CMN(sp);
		hdr_rlc_umts *rlchsp = HDR_RLC_UMTS(sp);

		rlchsp->frag_ = info_[pos].fraged_;
		rlchsp->ack_ = info_[pos].acked_;
		rlchsp->seqno_ = 0;
		rlchsp->bopno_ = bopno;
		rlchsp->eopno_ = eopno;
		rlchsp->psize_ = chsp->size_; //hack for now to remeber uid
		chsp->size_ = psize;
		chsp->size() += RLC_HDR_SZ;

		if (info_[pos].acked_ == 0){
			rlchsp->seqno_ = i;
			info_[pos].seqno_++;
			Scheduler& s = Scheduler::instance();
			if (rlcverbose_==1) fprintf(stderr,"<%d> %f RLC: sending NON-acked down. pkt uid=%d seqno %d\n",
					ip_ue_, NOW,chsp->uid_, rlchsp->seqno_);
			s.schedule(downtarget_, sp, delay_);
		}else{
			rlchsp->seqno_ = i;
			info_[pos].seqno_++;
			buf_->enque(sp);
			if (rlcverbose_==1) fprintf(stderr,"<%d> %f RLC: buffering frag. rlc_seqno=%d for pkt uid=%d buf_len=%d\n"
					, ip_ue_, NOW, rlchsp->seqno_,chsp->uid_, buf_->length());
		}
	}
	if (info_[pos].acked_==1)
		sendDownDATA(pos);

	return;
}

void RlcUmts::sendDownDATA(int pos)
{
	Packet* sp;

	// not more pkts of this flow to tx
	if (length_ >= buf_->length()) {
		length_ = 0;
		return;
	}

	if(!(sp = buf_->deque())){
		length_ = 0;
		return;
	}

	length_++;
	hdr_ip *ihsp = HDR_IP(sp);
	if (ihsp->flowid() != info_[pos].flow_) {
		buf_->enque(sp);
		sendDownDATA(pos);
	} else {
		hdr_rlc_umts *rlcsp = HDR_RLC_UMTS(sp);
		if(info_[pos].Txbuf_->length() < info_[pos].window_){
			info_[pos].Txbuf_->enque(sp->copy());
			if (rlcverbose_==1) fprintf (stderr, "<%d> %f RLC: Txbuf not full[%d]=%d,sending a data down, rlc_seq=%d from buf\n",
					ip_ue_, NOW, pos,info_[pos].Txbuf_->length(),rlcsp->seqno_);
			Scheduler& s = Scheduler::instance();
			s.schedule(downtarget_, sp, delay_);
			length_--;
			sendDownDATA(pos);
		}else{
			length_ = 0;
			if (rlcverbose_==1) fprintf (stderr, "<%d> %f RLC: Txbuf full. couldnt send rlc_seqno=%d \n",
					ip_ue_, NOW, rlcsp->seqno_);
			buf_->enqueHead(sp);
		}
	}
	return;
}

void RlcUmts::recvACK(Packet* p)
{
	int i, Txlen, first, last, temp;

	hdr_rlc_umts *rlch = HDR_RLC_UMTS(p);
	hdr_ip *ih = HDR_IP(p);

	i = look_for(ih->flowid());

	if (rlch->ackno_==info_[i].ackno_) {
		if (rlcverbose_==1)
		fprintf(stderr,"\n<%d> %f RLC: recvd duplicate RLC_ACK num %d until %d\n",
				            ip_ue_, NOW, rlch->ackno_,rlch->eopno_);
		first = rlch->bopno_;
		last = rlch->eopno_;
		for (temp=first; temp<last; temp++){
			sendDownDATAonACK(i, temp);
		}
		Packet::free(p);
		return;
	}
	else if(info_[i].ackno_ < rlch->ackno_){
		info_[i].ackno_ = rlch->ackno_;
	} else {
		Packet::free(p);
		return;
	}

	Txlen=info_[i].Txbuf_->length();
	if (rlcverbose_==1) fprintf(stderr,"<%d> %f RLC: recvd ACK num %d waiting %d buf length %d pos %d\n",
			ip_ue_, NOW, rlch->ackno_, info_[i].ackno_, Txlen, i);

	Packet* sp=info_[i].Txbuf_->head();

	//delete pkts with seqno < ackno
	for (int j=0; j<Txlen; j++){
		hdr_rlc_umts *rlchsp= HDR_RLC_UMTS(sp);
		if((rlchsp->seqno_)<info_[i].ackno_){
			info_[i].Txbuf_->remove(sp);
			if (rlcverbose_==1) fprintf(stderr, "<%d> %f RLC: deleted pkt from tx buffer; rlc seqno=%d TxLen %d\n",
					ip_ue_, NOW, rlchsp->seqno_, info_[i].Txbuf_->length());
		}
		sp = sp->next_;
	}

	if ((info_[i].Txbuf_->length() > 0) && (info_[i].Txbuf_->length() < info_[i].window_)){
			sendDownDATA(i);
	}
}

void RlcUmts::sendDownDATAonACK(int pos, int seqno)
{
	Packet* sp=info_[pos].Txbuf_->head();
	int Txlen_=info_[pos].Txbuf_->length();

	for(int i=0; i<Txlen_; i++){

		hdr_rlc_umts *rlchsp= HDR_RLC_UMTS(sp);

		if((rlchsp->seqno_) == seqno){
			if (rlcverbose_==1)
			fprintf(stderr,"<%d> %f RLC: retx pkt from txbuf seqno=%d, eopno=%d\n",
					ip_ue_, NOW,rlchsp->seqno_,rlchsp->eopno_);
			Scheduler& s = Scheduler::instance();
			s.schedule(downtarget_, sp->copy(), delay_);
			return;
		}
		sp = sp->next_;
	}
}

void RlcUmts::sendUpDATA(Packet* p, int pos)
{
	int flag;
	if (hdr_cmn::access(p)->error() > 0)
		drop(p);
	else{
		hdr_cmn *ch = HDR_CMN(p);
		hdr_rlc_umts *rlch = HDR_RLC_UMTS(p);
		hdr_ll *llch = HDR_LL(p);
		hdr_ip *iph = HDR_IP(p);

		if(info_[pos].acked_==0){
			if (rlch->seqno_ == info_[pos].unackseqno_){
                		info_[pos].unackseqno_++;

				if (rlch->seqno_ == rlch->eopno_ ){
					ch->size_ = rlch->psize_;
					if (rlcverbose_ == 1) fprintf(stderr ,"<%d> %f RLC: nonack. recvd all rlc frags for pktuid=%d ll_seqno=%d \n",
							ip_ue_, NOW, ch->uid_, llch->seqno_);
					Scheduler& s = Scheduler::instance();
					s.schedule(uptarget_, p, delay_);
				} else {
					if (rlcverbose_==1) fprintf(stderr ,"<%d> %f RLC: nonack. pkts not inseq. not sent up\n",ip_ue_, NOW);
					Packet::free(p);
                		}

            	} else if (rlch->seqno_ == hinfo_[pos].unackseqno_){
                		hinfo_[pos].unackseqno_++;

				if (rlch->seqno_ == rlch->eopno_ ){
					ch->size_ = rlch->psize_;
					if (rlcverbose_ == 1) fprintf(stderr ,"<%d> %f RLC: nonack. recvd all rlc frags for pktuid=%d ll_seqno=%d \n",
							ip_ue_, NOW, ch->uid_, llch->seqno_);
					Scheduler& s = Scheduler::instance();
					s.schedule(uptarget_, p, delay_);
				} else {
					if (rlcverbose_==1) fprintf(stderr ,"<%d> %f RLC: nonack. pkts not inseq. not sent up\n",ip_ue_, NOW);
					Packet::free(p);
                		}

			} else if ((rlch->seqno() == rlch->bopno()) && (rlch->bopno() == 0)){
				// in case wired--> wireless
				info_[pos].unackseqno_ = 1;

				if (rlch->seqno_ == rlch->eopno_ ){
					ch->size_ = rlch->psize_;
					if (rlcverbose_ == 1) fprintf(stderr ,"<%d> %f RLC: nonack. recvd all rlc frags for pktuid=%d ll_seqno=%d \n",
							ip_ue_, NOW, ch->uid_, llch->seqno_);
					Scheduler& s = Scheduler::instance();
					s.schedule(uptarget_, p, delay_);
				} else {
					if (rlcverbose_==1) fprintf(stderr ,"<%d> %f RLC: nonack. pkts not inseq. not sent up\n",ip_ue_, NOW);
					Packet::free(p);
                		}

			} else {
                		info_[pos].unackseqno_ = rlch->seqno_ + 1;
				Packet::free(p);
 			}
			return;

		}else{
			if(rlch->seqno_ == (info_[pos].rackno_+1)){
				info_[pos].rackno_++;
				info_[pos].numdups_ = 0;
				info_[pos].lastRx_ = p->copy();
				if ( rlch->seqno_ == rlch->eopno_ ){
					ch->size_ = rlch->psize_;
		  			if (rlcverbose_==1) fprintf(stderr , "<%d> %f RLC:ACK mode:send up to LL pktuid=%d llseqno=%d \n",
						ip_ue_, NOW,ch->uid_, llch->seqno_);
					Scheduler& s = Scheduler::instance();
					s.schedule(uptarget_, p, delay_);
				} else {
			   		if (rlcverbose_==1) fprintf(stderr ,"<%d> %f RLC:ACK mode: recvd rlc_seqno=%d for pkt uid=%d ll_seqno=%d \n"
					   ,ip_ue_, NOW, rlch->seqno_,  ch->uid_ , llch->seqno_);
				}
				flag=1;
				while(flag){
					flag=0;
					if(info_[pos].Rxbuf_->length()!=0){
						Packet* sp = info_[pos].Rxbuf_->head();
						int Rxlen_ = info_[pos].Rxbuf_->length();
						for(int i=1; i<=Rxlen_; i++){
							hdr_rlc_umts* rlcsp = HDR_RLC_UMTS(sp);
							if(rlcsp->seqno_ == (info_[pos].rackno_+1)){
								info_[pos].rackno_++;
								info_[pos].lastRx_ = sp->copy();
								flag=1;
								hdr_cmn* chsp=HDR_CMN(sp);
								if ( rlcsp->seqno_ == rlcsp->eopno_ ){
									chsp->size_ = rlcsp->psize_;
									if (rlcverbose_ == 1) fprintf(stderr,"<%d> %f RLC: pkt in rxbuf: send up uid=%d seqno=%d bopno=%d eopno=%d \n",
											ip_ue_, NOW,ch->uid_,rlch->seqno_,rlch->bopno_,rlch->eopno_);
									Scheduler& s = Scheduler::instance();
									s.schedule(uptarget_, sp->copy(), delay_);
								} else {
									if (rlcverbose_==1) fprintf(stderr , "<%d> %f RLC: pkt in rxbuf: drop uid=%d seqno=%d bopno=%d eopno=%d next_hop %d \n", ip_ue_, NOW,chsp->uid_,rlcsp->seqno_,rlcsp->bopno_,rlcsp->eopno_,chsp->next_hop());
								}
								info_[pos].Rxbuf_->remove(sp);
							}
							sp=sp->next_;
						}
					}
				}
				sendACK(p, pos);

			} else if ((handover_) && (rlch->seqno_ == (hinfo_[pos].rackno_+1))) {
				hinfo_[pos].rackno_++;
				hinfo_[pos].numdups_ = 0;
				if (rlcverbose_ == 1) fprintf(stderr , "<%d> %f RLC: hrackno=%d, src=%d \n",
					ip_ue_, NOW, hinfo_[pos].rackno_,Address::instance().get_nodeaddr(iph->saddr()));
				if ( rlch->seqno_ == rlch->eopno_ ){
					ch->size_ = rlch->psize_;
					if (rlcverbose_==1) fprintf(stderr , "<%d> %f RLC:ACK mode:send up to LL pktuid=%d llseqno=%d \n",
						ip_ue_, NOW,ch->uid_, llch->seqno_);
					Scheduler& s = Scheduler::instance();
					s.schedule(uptarget_, p, delay_);
				} else {
					if (rlcverbose_==1) fprintf(stderr ,"<%d> %f RLC:ACK mode: recvd rlc_seqno=%d for pkt uid=%d ll_seqno=%d \n"
					,ip_ue_, NOW, rlch->seqno_,  ch->uid_ , llch->seqno_);
				}
				flag=1;
				while(flag){
					flag=0;
					if(info_[pos].Rxbuf_->length()!=0){
						Packet* sp = info_[pos].Rxbuf_->head();
						int Rxlen_ = info_[pos].Rxbuf_->length();
						for(int i=1; i<=Rxlen_; i++){
							hdr_rlc_umts* rlcsp = HDR_RLC_UMTS(sp);
							if(rlcsp->seqno_ == (hinfo_[pos].rackno_+1)){
								hinfo_[pos].rackno_++;
								flag=1;
								hdr_cmn* chsp=HDR_CMN(sp);
								if ( rlcsp->seqno_ == rlcsp->eopno_ ){
									chsp->size_ = rlcsp->psize_;
									if (rlcverbose_ == 1) fprintf(stderr,"<%d> %f RLC: pkt in rxbuf: send up uid=%d seqno=%d bopno=%d eopno=%d \n",
											ip_ue_, NOW,ch->uid_,rlch->seqno_,rlch->bopno_,rlch->eopno_);
									Scheduler& s = Scheduler::instance();
									s.schedule(uptarget_, sp->copy(), delay_);
								} else {
									if (rlcverbose_==1) fprintf(stderr , "<%d> %f RLC: pkt in rxbuf: drop uid=%d seqno=%d bopno=%d eopno=%d next_hop %d \n",
										ip_ue_, NOW,chsp->uid_,rlcsp->seqno_,rlcsp->bopno_,rlcsp->eopno_,chsp->next_hop());
								}
								info_[pos].Rxbuf_->remove(sp);
							}
							sp=sp->next_;
						}
					}
				}
				sendACK(p, pos);

			}else if(rlch->seqno_ > (info_[pos].rackno_+1)){
				info_[pos].Rxbuf_->enque(p);
	     			if (info_[pos].numdups_ < 1){
					if (rlcverbose_==1) fprintf (stderr,"<%d> %f RLC: pkt out of seq, buf in rxbuf;send duplicate ackseqno=%d\n", ip_ue_, NOW,rlch->seqno_);
					sendACK(p, pos);
					info_[pos].numdups_ ++;

				}
			}
		}
	}
}

void RlcUmts::sendACK(Packet* p, int pos)
{

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

		Packet* sp = Packet::alloc();
		hdr_cmn *chsp = HDR_CMN(sp);
		hdr_rlc_umts *rlchsp = HDR_RLC_UMTS(sp);
		hdr_phy *phsp = HDR_PHY_UMTS(sp);
		hdr_ip *iphsp = HDR_IP(sp);

		chsp->next_hop_= Address::instance().get_nodeaddr(ch->prev_hop_);
		chsp->prev_hop_=Address::instance().get_nodeaddr(iph->daddr());
		chsp->channel_t() = CCCH;
		chsp->addr_type_ = ch->addr_type_;
		chsp->direction_ = hdr_cmn::DOWN;
		chsp->ptype_=PT_ACK;
		chsp->size_=6;
		chsp->iface()= -2;
		chsp->error()= 0;
		chsp->uid()= info_[pos].rackno_+1;

		iphsp->saddr() = iph->daddr();
		iphsp->daddr() = iph->saddr();
		iphsp->flowid() = iph->flowid();

		rlchsp->rlctype() = RLC_ACK;
		rlchsp->ackno_ = info_[pos].rackno_+1;
		rlchsp->bopno_ = info_[pos].rackno_+1;

		int len, vt;
		len=info_[pos].Rxbuf_->length();
		Packet* ptemp=info_[pos].Rxbuf_->head();
		if (ptemp != NULL) {
			struct hdr_rlc_umts *pth = HDR_RLC_UMTS(ptemp);
			vt = pth->seqno_;
			ptemp = ptemp->next_;
			for (int j=1; j<len; j++) {
				pth = HDR_RLC_UMTS(ptemp);
				if (pth->seqno_ < vt){
					vt = pth->seqno_;
				}
				ptemp = ptemp->next_;
			}
			rlchsp->eopno_ = vt;
		} else {
			rlchsp->eopno_ = rlchsp->bopno_+1;
		}

		phsp->da() = ph->sa();

		Scheduler& s = Scheduler::instance();
		s.schedule(downtarget_, sp, delay_);
		rtx_->start(info_[pos].flow_, 0.05);
		if (rlcverbose_==1)
		fprintf(stderr,"<%d> %f RLC: send RLCACK num %d in pos %d with first %d last %d\n",
				ip_ue_, NOW, rlchsp->ackno_, pos,rlchsp->bopno_,rlchsp->eopno_);
}

void RlcUmts::rTxHandler(int flow)
{
	if (rlcverbose_)
		printf("UE %d at %f RLC: Timer of flow %d expired. Retransmit ack\n", ip_ue_, NOW, flow);
	int pos = look_for(flow);
	sendACK(info_[pos].lastRx_, pos);
	return;
}

