/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1999, Federal University of Pernambuco - Brazil.
 * All rights reserved.
 *
 * License is granted to copy, to use, and to make and to use derivative
 * works for research and evaluation purposes.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Carlos Alberto Kamienski <cak@di.ufpe.br>
 *
 */

#include <fstream.h>
#include "dsshaper.h"
#include "scheduler.h"
#include "packet.h"

void DSShaperHandler::handle(Event *e)
{
	shaper_->resume();
}

static class DSShaperTclClass : public TclClass {
public:
	DSShaperTclClass() : TclClass("DSShaper") {}
	TclObject* create(int, const char*const*) {
                return (new DSShaper);
        }
} class_dsshaper ;

DSShaper::DSShaper() : Connector(), 
			   received_packets(0),
			   sent_packets(0),
			   shaped_packets(0),
			   dropped_packets(0),
			   max_queue_length(0),
			   flow_id_(0),
			   last_time(0),
                       sh_(this)
{

}

void DSShaper::recv(Packet *p, Handler *h)
{
	struct hdr_ip *iph = HDR_IP(p) ;
	
	if (iph->fid_ != flow_id_) {
//              Just send packets which fid_ do not match 
	        target_->recv(p,h);    
	        return;
	}        

	received_packets++;

	if (shape_queue.length() == 0) {
//          There are no packets being shapped. Tests profile.
	    if (in_profile(p)) {
 	        sent_packets++;
	        target_->recv(p,h);    
	    } else {
	        if (shape_packet(p))         
           	    schedule_packet(p);
//           	else
//                  Shaper is being used as a dropper
            } 
  	} else {          	    
//          There are packets being shapped. Shape this packet too.
            shape_packet(p);         
	}
}

bool DSShaper::shape_packet(Packet *p)
{
        if (shape_queue.length() >= max_queue_length) {
	    drop (p);
	    dropped_packets++;
	    return (false);
        } 
        shape_queue.enque(p);
        shaped_packets++;
	return (true);
}

void DSShaper::schedule_packet(Packet *p)
{
//      calculate time to wake up	          
        int packetsize = hdr_cmn::access(p)->size_ * 8 ;
        double delay = (packetsize - curr_bucket_contents)/peak_;
        Scheduler& s = Scheduler::instance();
	s.schedule(&sh_, p, delay);
}


void DSShaper::resume()
{
	Packet *p = shape_queue.deque();


	if (in_profile(p)) {
            sent_packets++;
            target_->recv(p,(Handler*) NULL);    
	} else {
//          This is not an expected error, because the packet was scheduled to wake up
//          exactly when it could be sent 
	    puts ("shaper/resume - This error should not occur!\n");
	    drop (p);
	    dropped_packets++;
        } 

	if (shape_queue.length() > 0) {
//         There are packets in the queue. Schedule the first one.
           Packet *first_p = shape_queue.lookup(0);
//         Got the first packet in the queue.
	   schedule_packet(first_p);         
        }   
} 



bool DSShaper::in_profile(Packet *p)
{

	update_bucket_contents() ;

	int packetsize = hdr_cmn::access(p)->size_ * 8 ; // in bits

	if (packetsize > curr_bucket_contents)
		return false;
	else {
		curr_bucket_contents -= packetsize ;
		return true ;
	}
}

void DSShaper::update_bucket_contents()
{
//      I'm using the token bucket implemented by Sean Murphy
        
	double current_time = Scheduler::instance().clock() ;
	
	double added_bits = (current_time - last_time) * peak_ ;
	curr_bucket_contents += (int) (added_bits + 0.5);
	if (curr_bucket_contents > burst_size_)
		curr_bucket_contents=burst_size_ ;
	last_time = current_time ;


}


int DSShaper::command(int argc, const char* const*argv)
{
	if (argc==2) {
		if (strcmp(argv[1],"reset-counters")==0) {
			reset_counters() ;
			return TCL_OK ;
		}
		if (strcmp(argv[1],"get-received-packets")==0) {
			Tcl& tcl = Tcl::instance();
			tcl.resultf("%d",received_packets);
			return TCL_OK ;
		}
		if (strcmp(argv[1],"get-sent-packets")==0) {
			Tcl& tcl = Tcl::instance();
			tcl.resultf("%d",sent_packets);
			return TCL_OK ;
 		}
		if (strcmp(argv[1],"get-shaped-packets")==0) {
			Tcl& tcl = Tcl::instance();
			tcl.resultf("%d",shaped_packets);
			return TCL_OK ;
		}
		if (strcmp(argv[1],"get-dropped-packets")==0) {
			Tcl& tcl = Tcl::instance();
			tcl.resultf("%d",dropped_packets);
			return TCL_OK ;
		}
	}
	if (argc==3) {
		if (strcmp("set-peak-rate", argv[1])==0) {
		    peak_ = atof(argv[2]);
			return TCL_OK ;
		}
		if (strcmp("set-burst-size", argv[1])==0) {
		    burst_size_ = curr_bucket_contents = atoi(argv[2]);
			return TCL_OK ;
		}
		if (strcmp("set-fid", argv[1])==0) {
		    flow_id_ = atoi(argv[2]);
			return TCL_OK ;
		}
		if (strcmp("set-queue-length", argv[1])==0) {
		    max_queue_length = atoi(argv[2]);
			return TCL_OK ;
		}
		return Connector::command(argc,argv) ;
	}
	
	return Connector::command(argc, argv) ;
}

void DSShaper::reset_counters()
{
	received_packets = sent_packets = shaped_packets = dropped_packets = 0 ;
}

