#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <new.h>
#include <iostream.h>

#include "ip.h"
#include "tcp.h"
#include "flags.h"

#include "tcp-actcc.h"
inline int max(int a, int b) {return (a > b ) ? a : b;}

int hdr_act_tcp::offset_;

static class ActiveTcpAgentClass : public TclClass {
public:
    ActiveTcpAgentClass() : TclClass("Agent/TCP/Active") {};
    TclObject* create(int, const char *const* ) {
	return (new ActiveTcpAgent());
    }
} class_tcp_active;

static class ActiveTcpHeaderClass : public PacketHeaderClass {
public:
    // The name can only be one level deep as of ns 2.1b5.  Packet
    // header space assignment continues to be weird.  unexplained
    // errors result if it's done wrong.  See (mostly) ns-packet.tcl
    ActiveTcpHeaderClass() :
	PacketHeaderClass("PacketHeader/Active", sizeof(hdr_act_tcp)) {
	bind_offset(&hdr_act_tcp::offset_);
    }
} class_activetcphdr;



void ActiveTcpAgent::recv(Packet *pkt, Handler* handler)
{
    hdr_cmn *cmn_h = HDR_CMN(pkt);		// Common header
    hdr_act_tcp *act_tcp_h = HDR_ACT_TCP(pkt);	// Active header
    Scheduler &s = Scheduler::instance();	// To get the current sim time
    double now = (&s) ? s.clock() : 0.0;	// The current sim time
    double est_rtt = ((t_srtt_ >> 3) + t_rttvar_) * tcp_tick_ ;
                                                // current rtt estimate
    int how;					// How to close the window

    if ( cmn_h->ptype() == PT_ACTIVE ) {
	active_packets_++;

	// first offense 

	if ( highest_ack_ >= recover_ && now > last_active_close_ + est_rtt) {

	    /* Just reduce to window size at loss */
	    second = 0;
	    cwnd_ = act_tcp_h->window();
	    if ( cwnd_ < 2.0 ) cwnd_ = 2.0;

	    ssthresh_ = act_tcp_h->window();
	    if (ssthresh_ < 2) ssthresh_ = 2;
	    
	    if (now-last_active_close_ < minbetween)
		minbetween = now - last_active_close_;

	    last_active_close_ = now;
	    actid = act_tcp_h->id();
//	    active_closes_++;

	    recover_ = maxseq_;
	    if ( act_tcp_h->killed() <= highest_ack_ ) {
		maxseq_ = highest_ack_+1;
	    }
	    else {
		maxseq_ = act_tcp_h->killed();
	    }
	    reset_rtx_timer(0,0);
	    t_seqno_ = maxseq_;
	    send_much(0,0,maxburst_);
	}
	else
	    if ( second ==  0 ) {
		second ++;
		/* Reduce window size with congestion */
		/* These are halved below */
		cwnd_ = act_tcp_h->window();
		if ( cwnd_ < 2.0 ) cwnd_ = 2.0;

		ssthresh_ = act_tcp_h->window();
		if (ssthresh_ < 2) ssthresh_ = 2;
	    
		if (now-last_active_close_ < minbetween)
		    minbetween = now - last_active_close_;

		last_active_close_ = now;
		actid = act_tcp_h->id();
		active_closes_++;
		dupacks_ = 0;

		recover_ = maxseq_;
		if ( act_tcp_h->ss() )
		    how = CLOSE_CWND_ONE;
		else
		    how = CLOSE_CWND_ONE|CLOSE_SSTHRESH_HALF;

		if ( act_tcp_h->killed() <= highest_ack_ ) {
		    slowdown(how);
		    maxseq_ = highest_ack_+1;
		}
		else {
		    slowdown(how);
		    maxseq_ = act_tcp_h->killed();
		}
		t_seqno_ = maxseq_;
//		reset_rtx_timer(0,0);
		send_much(0,0,maxburst_);
	    }

	Packet::free(pkt);
    }
    else {
	TcpAgent::recv(pkt, handler);
    }
}

void ActiveTcpAgent::output_helper(Packet *p) {
    hdr_act_tcp *act = HDR_ACT_TCP(p);	// The active header

    act->window() = max(window(), int(ssthresh_));
    act->ss() = window() < int(ssthresh_);
    act->src = this;
    act->id() = actid;
    TcpAgent::output_helper(p);
}

void ActiveTcpAgent::timeout(int tno) {
    Scheduler &s = Scheduler::instance();	// To get the current sim time
    double now = (&s) ? s.clock() : 0.0;	// The current sim time
    double est_rtt = ((t_srtt_ >> 3) + t_rttvar_) * tcp_tick_ ;
                                                // current rtt estimate
    if ( now < last_active_close_ + est_rtt ) {
	// this is probably a disruption caused by an active close
	reset_rtx_timer(0,0);
	send_much(0, 0, maxburst_);
    }
    TcpAgent::timeout(tno);
}
