/***************************************************************************
                    packet.c  -  all the ed2k packet stuff
                    ---------------------------------------
    begin                : Thu May 17 2001
    copyright            : (C) 2001 by Tim-Philipp Müller
    email                : t.i.m@orange.net
 ***************************************************************************/

/*
 *   Functions to
 *    - send an eDonkey2000 packet
 *    - check if data has been received
 *    - extract data from a eDonkey2000 packet
 *
 */

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "packet.h"

#include "statusbar.h"
#include "global.h"
#include "options.h"
#include "status_page.h"

#include <sys/time.h>
#include <unistd.h>

// global variables


// local variables

#define ED2K_BYTE 0xe3	// = 227, header byte at beginning of every packet


// functions


// packet_append
//
// returns FALSE on error, otherwise TRUE
//

gboolean
packet_append (ramfile **packet, const gchar *buffer, gint length)
{
	g_return_val_if_fail (packet!=NULL, FALSE);
	g_return_val_if_fail (buffer!=NULL, FALSE);

	if (*packet==NULL)
	{
		*packet = ramfile_new (length);
		g_return_val_if_fail (*packet!=NULL, FALSE);
	}
	ramfile_write(*packet, buffer, length);
	return TRUE;
}

gboolean
packet_header_is_valid (ramfile *packet, guint32 *datalen)
{
	guint8	donkeybyte = 0;

	g_return_val_if_fail (packet!=NULL, FALSE);

	if ( ramfile_get_size(packet) < 5 )
		return FALSE;

	ramfile_peek(packet, &donkeybyte, 0, sizeof(guint8));

	if (donkeybyte != ED2K_BYTE)
		return FALSE;

	if (datalen)
		*datalen = packet_get_data_size (packet);

	return TRUE;
}

guint32
packet_get_data_size (ramfile *packet)
{
	guint32	datalen;
	g_return_val_if_fail (packet!=NULL, 0);
	if (ramfile_get_size(packet)<5)
		return 0;
	ramfile_peek(packet, (guint8*)&datalen, 1, 4);
	return GUINT32_FROM_LE(datalen);
}

gboolean
packet_is_complete (ramfile *packet)
{
	static ramfile *last_packet_checked = NULL;
	static guint32  last_datasize = 0;

	g_return_val_if_fail ( packet != NULL, FALSE );

	if ( last_packet_checked == packet  &&  last_datasize > 0 )
			return ( ramfile_get_size(packet) >= (5 + last_datasize) );

	last_packet_checked = packet;
	last_datasize = 0;

	// minimal length of a complete packet is 6 (5 bytes header + 1 byte command)
	if ( ramfile_get_size(packet) >= 6 )
	{
		last_datasize = packet_get_data_size(packet);

		if ( last_datasize > 0 )
			return ( ramfile_get_size(packet) >= (5 + last_datasize) );
	}

	return FALSE;
}


// packet_get_next_packet_data
//
// If a packet is complete, it might have some data at
//    the end which belongs to the next packet. This
//    routine will get that data and put it at the
//    beginning of a new packet and return that.
// If there is no superfluous data, it will return NULL
//
// call this only after packet_is_complete() has checked out fine!

ramfile *
packet_get_next_packet_data (ramfile *packet)
{
	ramfile	*nextp;
	guint32	 datasize, toomuch;

	g_return_val_if_fail (packet!=NULL, NULL);

	datasize = packet_get_data_size (packet);

	if (datasize==0)
		return NULL;

	// minimal length of a complete packet is
	//   1 byte + chunks*4 bytes [command chunk lengths]
	//      + chunks*1 byte [the command chunks]
	toomuch = ramfile_get_size(packet) - (5+datasize);

	if (toomuch==0)
		return NULL;

	nextp = ramfile_new (1);
	ramfile_write (nextp, ramfile_get_buffer(packet)+5+datasize, toomuch);
	return nextp;
}


/******************************************************************************
 *
 *   packet_left_from_pos
 *
 *   Returns the number of bytes left from the current
 *    position in the given ed2k packet
 *
 ***/

gint
packet_left_from_pos (ramfile *packet)
{
	g_return_val_if_fail ( packet != NULL, 0 );
	g_return_val_if_fail ( ramfile_get_pos(packet) <= ramfile_get_size(packet), 0 );

	return ((packet_get_data_size(packet)+5) - ramfile_get_pos(packet));
}
