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

/* Timers */
void FCTimer::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 FCTimer::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 FCTimer::handle(Event *e)
{
	busy_ = 0;
	paused_ = 0;
	stime = 0.0;
	rtime = 0.0;

      queue_->fcHandler(e);
}

/******************************** FCQueue *************************************/

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

int FCQueue::verbose_ = {0};

// Constructor
FCQueue::FCQueue() : DropTail(), qTimer_(this)
{
	tti_ = TTI;		// set tti_ to 10 ms
	int i;
	for (i=0; i<MAX_NUM_FLOWS; i++) {
		ifqc[i].flowid_ = -1;
		ifqc[i].bytes_per_tti_ = 0;
		ifqc[i].len = 0;
	}

	ip_ue_ = -1;
	bind("verbose_",&verbose_);
	intr = intr_ ;
	qTimer_.start((Packet *) (& intr_), 0);	// start timer
}

int FCQueue::command(int argc, const char*const* argv)
{
	return DropTail::command(argc, argv);
}

void FCQueue::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;
}

// return position in ifqc[] for flow
int FCQueue::get_pos(int flow)
{
	int i;
	for (i=0; i<MAX_NUM_FLOWS; i++) {
		if (ifqc[i].flowid_ == flow){
			return (i);	// flow found
		}
	}
	return (-1);	// not found
}

// register and de-register for a flow
void FCQueue::reg(int flow, double l)
{
	int id, len;
	id = get_pos(flow);
	if (l == 0) { // drop all the packets stored in the queue belonging to the flow
		len = q_->length();
		while (len > 0) {
			Packet * prov = q_->deque();
			struct hdr_ip *ihprov = HDR_IP(prov);
			if (ihprov->flowid()== ifqc[id].flowid_) {
				drop(prov);
			} else {
				q_->enque(prov);
			}
			--len;
		}
		ifqc[id].bytes_per_tti_ = 0;
		ifqc[id].flowid_ = -1;
		ifqc[id].len = 0;
		return;
	}
	if (id < 0) { // flow not registered, proceed to register
		for (id=0; id < MAX_NUM_UE; id++){
			if (ifqc[id].flowid_ < 0) {
				ifqc[id].flowid_ = flow;
				break;
			}
		}
	}
	// update ifqc[] fields for the flow
	ifqc[id].bytes_per_tti_ = l;
	ifqc[id].len = 0;
	return;
}

// handler of FCTimer, executed each tti_
void FCQueue::fcHandler(Event *e)
{
	int l, pos;
	// Restart timer for next tti.
	qTimer_.start((Packet *)e, tti_);

	l = q_->length();	// take length of the queue

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

		pos = get_pos(ihprov->flowid());

		if (ifqc[pos].len < ifqc[pos].bytes_per_tti_){	// allow to tx this packet
			if (verbose_) printf("UE %d at %f IFQ: ********** sending pkt of size: %d free len: %f and uid: %d bpertti %f\n",
				ip_ue_, NOW, chprov->size(), ifqc[pos].len, chprov->uid(), ifqc[pos].bytes_per_tti_);
			ifqc[pos].len += chprov->size();	// update len
			target_->recv(prov, this);		// transmit the packet downwards
		} else {	// enqueue again the packet
			q_->enque(prov);
		}
		--l;
	}

	// update len in each tti.
	for (pos=0; pos<MAX_NUM_FLOWS; pos++) {
		if ((ifqc[pos].flowid_ != -1) && (ifqc[pos].len > 0)) {
			ifqc[pos].len -= ifqc[pos].bytes_per_tti_;
			if (ifqc[pos].len < 0) ifqc[pos].len = 0;
		}
	}
	return;
}

