/* -*-	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 "bsfc-queue.h"
#include <stdio.h>

/* Timers */
void BsFCTimer::start(Packet *p, double time)
{
	Scheduler &s = Scheduler::instance();
	assert(busy_ == 0);

	busy_ = 1;
	paused_ = 0;
	stime = s.clock();
	rtime = time;
	assert(rtime >= 0.0);

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

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

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

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

void BsFCTimer::handle(Event *e)
{
	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;

      queue_->bsfcHandler(e);
}

// TCL Class
static class BsFCQueueClass : public TclClass {
public:
  BsFCQueueClass() : TclClass("Queue/DropTail/BsFCQueue") {}
  TclObject* create(int, const char*const*) {
    return (new BsFCQueue);
  }
} class_BsFCQueue;

int BsFCQueue::verbose_ = {0};

// Contructor
BsFCQueue::BsFCQueue() : DropTail(), qTimer_(this), callback_(0)
{
	int i;
	ip_nodeb_ = -1;
	tti_ = TTI;	// set tti_ to 10 ms

	intr = intr_ ;	// for timer

	for (i=0; i<MAX_NUM_UE; i++) {
		users_[i].bytes_per_tti_ = 0;
		users_[i].dest_ = -1;
		users_[i].len_ = 0;
	}

	bind("verbose_",&verbose_);
	qTimer_.start((Packet *) (& intr_), 0);	// start timer
}

int BsFCQueue::command(int argc, const char*const* argv)
{
	Tcl& tcl = Tcl::instance();
	if (argc == 3) {
		if (strcmp(argv[1], "callback") == 0) {
			callback_ = (NsObject*) TclObject::lookup(argv[2]);
                  assert(callback_);
			return (TCL_OK);
		}
	}
	return DropTail::command(argc, argv);
}

void BsFCQueue::recv(Packet* p, Handler*)
{
	hdr_cmn *ch = HDR_CMN(p);

	if (ch->channel_t() == DTCH) {	// if the packet is user data
		if (q_->length() < limit())	// check if the queue is full
			q_->enque(p);		// enque the packet
		else
			drop(p);			// queue full, drop the packet
	} else {
		target_->recv(p, this);		// signalling
	}
	return;
}

// returns position in users_[] for a UE
int BsFCQueue::find(nsaddr_t dir)
{
 	int id;
	for (id=0; id < MAX_NUM_UE; id++){
		if (users_[id].dest_ == dir){
			return (id);	// UE found
		}
	}
	return(-1);	// UE not found
}

// register and de-register for a UE
void BsFCQueue::reg(nsaddr_t dir, double l)
{
	int id, len;
	id = find(dir);
	if (l == 0) { // send upwards all the packets stored in the queue belonging to the UE
		len = q_->length();
		if (verbose_) printf("Nodeb %d at %f IFQ: Pkts going up because of handover procedure or because of end of tx\n",
		 	ip_nodeb_, NOW);
		Scheduler& s = Scheduler::instance();
		while (len > 0) {
			Packet * prov = q_->deque();
			struct hdr_ip *ihprov = HDR_IP(prov);
			struct hdr_cmn *chprov = HDR_CMN(prov);
			if (ihprov->daddr()== users_[id].dest_) {
				chprov->channel_t() = DTCH;
				chprov->direction() = hdr_cmn::UP;
				chprov->ptype() = PT_CBR;
				ihprov->ttl() = 200;
				s.schedule(callback_, prov->copy(), 0.001);
				Packet::free(prov);
			} else {
				q_->enque(prov);
			}
			--len;
		}
		users_[id].bytes_per_tti_ = 0;
		users_[id].dest_ = -1;
		users_[id].len_ = 0;
		return;
	}
	if (id < 0) { // UE not registered, proceed
		for (id=0; id < MAX_NUM_UE; id++){
			if (users_[id].dest_ < 0) {
				users_[id].dest_ = dir;
				break;
			}
		}
	}
	// update users_[] fields for the UE
	users_[id].bytes_per_tti_ = l;
	users_[id].len_ = 0;
	return;
}

// handler of BsFCTimer, executed each tti_
void BsFCQueue::bsfcHandler(Event *e)
{
 	int l, id;
	// Restart timer for next tti.
	qTimer_.start((Packet *)e, tti_);

	l = q_->length();

	while (l > 0) { // fo through all the queue
		Packet * prov = q_->deque();
		struct hdr_cmn *chprov = HDR_CMN(prov);
		struct hdr_ip *ihprov = HDR_IP(prov);

		id = find(ihprov->daddr());	// find the UE for the packet
		users_[id].len_ -= chprov->size(); // update len

		if (users_[id].len_ < users_[id].bytes_per_tti_) { // allow to tx this packet
			if (verbose_) printf("Nodeb %d at %f IFQ: ***************** pkt for %d with size: %d free space: %f and uid: %d, bytes_tti %f\n",
				ip_nodeb_, NOW, ihprov->daddr(), chprov->size(), users_[id].len_, chprov->uid(), users_[id].bytes_per_tti_);
			users_[id].len_ += chprov->size();	// update len
			target_->recv(prov->copy(), this);	// transmit the packet downwards
			Packet::free(prov);
		} else { // enqueue again the packet
			q_->enque(prov);
		}
		--l;
	}

	// update len in each tti.
	for (id=0; id < MAX_NUM_UE; id++) {
		if ((users_[id].dest_ != -1) && (users_[id].len_ > 0)) {
			users_[id].len_ -= users_[id].bytes_per_tti_;
			if (users_[id].len_ < 0) users_[id].len_ = 0;
		}
	}
	return;
}

