/* 
 * connection.cpp
 *
 * Copyright (c) 2004-2006,2008 Arnout Engelen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */


#include <iostream>
#include <cassert>
#include <malloc.h>
#include "nethogs.h"
#include "connection.h"
#include "process.h"

ConnList * connections = NULL;

void PackList::add (Packet * p)
{
	if (content == NULL)
	{
		content = new PackListNode (new Packet (*p));
		return;
	}

	if (content->val->time.tv_sec == p->time.tv_sec)
	{
		content->val->len += p->len;
		return;
	}

	/* store copy of packet, so that original may be freed */
	content = new PackListNode(new Packet (*p), content);
}

/* sums up the total bytes used and removes 'old' packets */
u_int32_t PackList::sumanddel (timeval t)
{
	u_int32_t retval = 0;
	PackListNode * current = content;
	PackListNode * previous = NULL;

	while (current != NULL) 
	{
		//std::cout << "Comparing " << current->val->time.tv_sec << " <= " << t.tv_sec - PERIOD << endl;
		if (current->val->time.tv_sec <= t.tv_sec - PERIOD)
		{
			if (current == content)
				content = NULL;
			else if (previous != NULL)
				previous->next = NULL;
			delete current;
			return retval;
		}
		retval += current->val->len;
		previous = current;
		current = current->next;
	}
	return retval;
}

/* packet may be deleted by caller */
Connection::Connection (Packet * packet)
{
	assert (packet != NULL);
	connections = new ConnList (this, connections);
	sent_packets = new PackList ();
	recv_packets = new PackList ();
	sumSent = 0;
	sumRecv = 0;
	if (DEBUG)
	{
		std::cout << "New connection, with package len " << packet->len << std::endl;
	}
	if (packet->Outgoing())
	{
		sumSent += packet->len;
		sent_packets->add(packet);
		refpacket = new Packet (*packet);
	} else {
		sumRecv += packet->len;
		recv_packets->add(packet);
		refpacket = packet->newInverted();
	}
	lastpacket = packet->time.tv_sec;
	if (DEBUG)
		std::cout << "New reference packet created at " << refpacket << std::endl;
}

Connection::~Connection ()
{
	if (DEBUG)
		std::cout << "Deleting connection" << std::endl;
	/* refpacket is not a pointer to one of the packets in the lists
	 * so deleted */
	delete (refpacket);
    	if (sent_packets != NULL)
		delete sent_packets;
    	if (recv_packets != NULL)
		delete recv_packets;

	ConnList * curr_conn = connections;
	ConnList * prev_conn = NULL;
	while (curr_conn != NULL)
	{
		if (curr_conn->getVal() == this)
		{
			ConnList * todelete = curr_conn;
			curr_conn = curr_conn->getNext();
			if (prev_conn == NULL)
			{
				connections = curr_conn;
			} else {
				prev_conn->setNext(curr_conn);
			}
			delete (todelete);
		}
		else
		{
			prev_conn = curr_conn;
			curr_conn = curr_conn->getNext();
		}
	}
}

/* the packet will be freed by the calling code */
void Connection::add (Packet * packet)
{
	lastpacket = packet->time.tv_sec;
	if (packet->Outgoing())
	{
		if (DEBUG)
		{
			std::cout << "Outgoing: " << packet->len << std::endl;
		}
		sumSent += packet->len;
		sent_packets->add (packet);
	} 
	else 
	{
		if (DEBUG)
		{
			std::cout << "Incoming: " << packet->len << std::endl;
		}
		sumRecv += packet->len;
		if (DEBUG)
		{
			std::cout << "sumRecv now: " << sumRecv << std::endl;
		}
		recv_packets->add (packet);
	}
}

Connection * findConnectionWithMatchingSource(Packet * packet) {
	assert(packet->Outgoing());

	ConnList * current = connections;
	while (current != NULL)
	{
		/* the reference packet is always outgoing */
		if (packet->matchSource(current->getVal()->refpacket))
		{
			return current->getVal();
		}

		current = current->getNext();
	}
	return NULL;
}

Connection * findConnectionWithMatchingRefpacketOrSource(Packet * packet) {
	ConnList * current = connections;
	while (current != NULL)
	{
		/* the reference packet is always *outgoing* */
		if (packet->match(current->getVal()->refpacket))
		{
			return current->getVal();
		}

		current = current->getNext();
	}
	return findConnectionWithMatchingSource(packet);
}

/* 
 * finds connection to which this packet belongs.
 * a packet belongs to a connection if it matches
 * to its reference packet 
 */
Connection * findConnection (Packet * packet)
{
	if (packet->Outgoing())
		return findConnectionWithMatchingRefpacketOrSource(packet);
	else
	{
		Packet * invertedPacket = packet->newInverted();
		Connection * result = findConnectionWithMatchingRefpacketOrSource(invertedPacket);

		delete invertedPacket;
		return result;
	}
}

/*
 * Connection::sumanddel
 *	
 * sums up the total bytes used
 * and removes 'old' packets. 
 *
 * Returns sum of sent packages (by address)
 *	   sum of recieved packages (by address)
 */
void Connection::sumanddel (timeval t, u_int32_t * recv, u_int32_t * sent)
{
    (*sent)=(*recv)=0;

    *sent = sent_packets->sumanddel(t);
    *recv = recv_packets->sumanddel(t);
}
