/**
 * delay.cc
 */

#ifndef lint
static const char rcsid[] =
    "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/link/delay.cc,v 1.26 1999/09/09 03:22:36 salehi Exp $ (LBL)";
#endif

#include "delay.h"
#include "mcast_ctrl.h"
#include "ctrMcast.h"

#include "random.h"
#include "tcp.h"

static class LinkDelayClass : public TclClass {
public:
	LinkDelayClass() : TclClass("DelayLink") {}
	TclObject* create(int /* argc */, const char*const* /* argv */) {
		return (new LinkDelay);
	}
} class_delay_link;

LinkDelay::LinkDelay() : dynamic_(0), itq_(0), delayMode_(0)
{
	bind_bw("bandwidth_", &bandwidth_);
	bind_time("delay_", &delay_);

	bind("delayMode_", &delayMode_);
	printf("%x LinkDelay created\n", this);
}

double LinkDelay::getDelay(Packet* p)
{
	if(delayMode_ == LD_DelayMode_Constant
    //wan TODO:  this condition is necessary.  Otherwise, the delayed data packet will cause dup-acks!!
        || hdr_cmn::access(p)->ptype() != PT_ACK)
	{
		//del printf("%s@%x: LinkDelay::getDelay(): delaymode(%i), delay(%f)\n", name(), this, delayMode_, delay_);
		return delay_;
	}
	else //if(delayMode_ == LD_DelayMode_Random)
	{
        //wan modify to generate varying delay.
        double dVar = Random::uniform(0.0, 2.0);
        double dPdelay = delay_ * dVar;

        static double s = 0;
        static int count = 0;
        s += dPdelay;
        count ++;

        printf("%s: LinkDelay::getDelay(): delay_(%f), pdelay(%f), avgdelay(%f), var(%f)\n", name(), delay_, dPdelay, s/count, dVar);

        return dPdelay;
	}
}

int LinkDelay::command(int argc, const char*const* argv)
{
	if (argc == 2) {
		if (strcmp(argv[1], "isDynamic") == 0) {
			dynamic_ = 1;
			itq_ = new PacketQueue();
			return TCL_OK;
		}
	} else if (argc == 6) {
		if (strcmp(argv[1], "pktintran") == 0) {
			int src = atoi(argv[2]);
			int grp = atoi(argv[3]);
			int from = atoi(argv[4]);
			int to = atoi(argv[5]);
			pktintran (src, grp);
			Tcl::instance().evalf("%s puttrace %d %d %d %d %d %d %d %d", name(), total_[0], total_[1], total_[2], total_[3], src, grp, from, to);
			return TCL_OK;
		}
	}

	return Connector::command(argc, argv);
}

void LinkDelay::recv(Packet* p, Handler* h)
{
    //wan testing
    //printf("%f [%s@%x]: LinkDelay::recv(): type %i seqno %9i\n", Scheduler::instance().clock(), name(),
    //    this, hdr_cmn::access(p)->ptype(), hdr_tcp::access(p)->seqno());

	double txt = txtime(p);
    Scheduler& s = Scheduler::instance();
	if (dynamic_) {
		Event* e = (Event*)p;
		e->time_= txt + getDelay(p); // delay_;
		itq_->enque(p); // for convinience, use a queue to store packets in transit
		s.schedule(this, p, txt + getDelay(p)); // delay_);
        //del printf("LinkDelay::recv(Packet* p, Handler* h), dynamic\n");
	} else {
		s.schedule(target_, p, txt + getDelay(p)); //delay_);
        //del printf("LinkDelay::recv(Packet* p, Handler* h), not-dynamic \n");
	}
	s.schedule(h, &intr_, txt);
}

void LinkDelay::send(Packet* p, Handler*)
{
	target_->recv(p, (Handler*) NULL);
}

void LinkDelay::reset()
{
	Scheduler& s= Scheduler::instance();

	if (itq_ && itq_->length()) {
		Packet *np;
		// walk through packets currently in transit and kill 'em
		while ((np = itq_->deque()) != 0) {
			s.cancel(np);
			drop(np);
		}
	}
}

void LinkDelay::handle(Event* e)
{
	Packet *p = itq_->deque();
	assert(p->time_ == e->time_);
	send(p, (Handler*) NULL);
}

void LinkDelay::pktintran(int src, int group)
{
	int reg = 1;
	int prune = 30;
	int graft = 31;
	int data = 0;
	for (int i=0; i<4; i++) {
		total_[i] = 0;
	}

	if (! dynamic_)
		return;

	int len = itq_->length();
	while (len) {
		len--;
		Packet* p = itq_->lookup(len);
		hdr_ip* iph = hdr_ip::access(p);
		if (iph->flowid() == prune) {
			if (iph->saddr() == src && iph->daddr() == group) {
				total_[0]++;
			}
		} else if (iph->flowid() == graft) {
			if (iph->saddr() == src && iph->daddr() == group) {
				total_[1]++;
			}
		} else if (iph->flowid() == reg) {
			hdr_CtrMcast* ch = hdr_CtrMcast::access(p);
			if (ch->src() == src+1 && ch->group() == group) {
				total_[2]++;
			}
		} else if (iph->flowid() == data) {
			if (iph->saddr() == src+1 && iph->daddr() == group) {
				total_[3]++;
			}
		}
	}
        //printf ("%f %d %d %d %d\n", Scheduler::instance().clock(), total_[0], total_[1], total_[2],total_[3]);
}
    