/***************************************************************************
                          buffer.cpp  -  description
                             -------------------
    begin                : Thu Jun 6 2002

    Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
    Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
    Kopete    (c) 2002-2003 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * 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 <kdebug.h>
#include <tdeapplication.h>
#include "buffer.h"

#include <ctype.h>

Buffer::Buffer()
{
	mReadPos=0;
}

Buffer::Buffer( const Buffer& other )
{
	mBuffer.duplicate( other.mBuffer );
	mReadPos = other.mReadPos;
}

Buffer::Buffer(const char *b, TQ_ULONG len)
{
	mBuffer.duplicate(b, len);
	mReadPos=0;
}

Buffer::Buffer( const TQByteArray& data )
{
	mBuffer.duplicate( data );
	mReadPos = 0;
}


Buffer::~Buffer()
{
}


int Buffer::addByte(const BYTE b)
{
	expandBuffer(1);
	mBuffer[mBuffer.size()-1] = b;

	return mBuffer.size();
}

int Buffer::addLEByte(const BYTE b)
{
	expandBuffer(1);
	mBuffer[mBuffer.size()-1] = ((b) & 0xff);

	return mBuffer.size();
}


int Buffer::addWord(const WORD w)
{
	expandBuffer(2);
	mBuffer[mBuffer.size()-2] = ((w & 0xff00) >> 8);
	mBuffer[mBuffer.size()-1] = (w & 0x00ff);

	return mBuffer.size();
}

int Buffer::addLEWord(const WORD w)
{
	expandBuffer(2);
	mBuffer[mBuffer.size()-2] = (unsigned char) ((w >> 0) & 0xff);
	mBuffer[mBuffer.size()-1] = (unsigned char) ((w >> 8) & 0xff);

	return mBuffer.size();
}


int Buffer::addDWord(const DWORD dw)
{
	expandBuffer(4);
	mBuffer[mBuffer.size()-4] = (dw & 0xff000000) >> 24;
	mBuffer[mBuffer.size()-3] = (dw & 0x00ff0000) >> 16;
	mBuffer[mBuffer.size()-2] = (dw & 0x0000ff00) >> 8;
	mBuffer[mBuffer.size()-1] = (dw & 0x000000ff);

	return mBuffer.size();
}

int Buffer::addLEDWord(const DWORD dw)
{
	expandBuffer(4);
	mBuffer[mBuffer.size()-4] = (unsigned char) ((dw >> 0) & 0xff);
	mBuffer[mBuffer.size()-3] = (unsigned char) ((dw >>  8) & 0xff);
	mBuffer[mBuffer.size()-2] = (unsigned char) ((dw >> 16) & 0xff);
	mBuffer[mBuffer.size()-1] = (unsigned char) ((dw >> 24) & 0xff);

	return mBuffer.size();
}

int Buffer::addString(TQByteArray s)
{
	unsigned int pos = mBuffer.size();
	int len = s.size();
	expandBuffer(len);
	
	for ( int i = 0; i < len; i++ )
		mBuffer[pos + i] = s[i];
	
	return mBuffer.size();
}

int Buffer::addString(TQByteArray s, DWORD len)
{
	Q_UNUSED( len );
	return addString( s );
}

int Buffer::addString( const char* s, DWORD len )
{
	TQByteArray qba;
	qba.duplicate( s, len );
	return addString( qba );
}

int Buffer::addString(const unsigned char* s, DWORD len)
{
	TQByteArray qba;
	qba.duplicate( (const char*) s, len );
	return addString( qba );
}

int Buffer::addLEString(const char *s, const DWORD len)
{
	unsigned int pos = mBuffer.size();
	expandBuffer(len);
	//concatenate the new string onto the buffer
	for(unsigned int i=0; i<len; i++)
	{
		mBuffer[pos+i]=((s[i]) & 0xff);
	}
	return mBuffer.size();
}


void Buffer::clear()
{
	mBuffer.truncate( 0 );
	mReadPos=0;
}

int Buffer::addTLV( const TLV& t )
{
	return addTLV( t.type, t.length, t.data );
}

int Buffer::addTLV(WORD type, WORD len, const char *data)
{

	addWord(type);
	addWord(len);
	return addString(data,len);
}

int Buffer::addLETLV(WORD type, WORD len, const char *data)
{
	addLEWord( type );
	addLEWord( len );
	return addString( data, len );
}

BYTE Buffer::getByte()
{
	BYTE thebyte = 0x00;

	if(mReadPos < mBuffer.size())
	{
		thebyte = mBuffer[mReadPos];
		mReadPos++;
	}
	else
		kdDebug(14150) << "Buffer::getByte(): mBuffer empty" << endl;

	return thebyte;
}

void Buffer::skipBytes( int bytesToSkip )
{
	if (mReadPos < mBuffer.size())
		mReadPos += bytesToSkip;
}

BYTE Buffer::getLEByte()
{
	BYTE b = getByte();
	return (b & 0xff);
}

WORD Buffer::getWord()
{
	WORD theword, theword2, retword;
	theword = getByte();
	theword2 = getByte();
	retword = (theword << 8) | theword2;
	return retword;
}

WORD Buffer::getLEWord()
{
	WORD theword1, theword2, retword;
	theword1 = getLEByte();
	theword2 = getLEByte();
	retword = (theword2 << 8) | theword1;
	return retword;
}

DWORD Buffer::getDWord()
{
	DWORD word1, word2;
	DWORD retdword;
	word1 = getWord();
	word2 = getWord();
	retdword = (word1 << 16) | word2;
	return retdword;
}

DWORD Buffer::getLEDWord()
{
	DWORD word1, word2, retdword;
	word1 = getLEWord();
	word2 = getLEWord();
	retdword = (word2 << 16) | word1;
	return retdword;
}

void Buffer::setBuf(char *b, const WORD len)
{
	kdDebug(14150) << k_funcinfo << "Called." << endl;
	
	mBuffer.duplicate(b, len);
	mReadPos = 0;
}

TQByteArray Buffer::getBlock(WORD len)
{
	TQByteArray ch( len );
	for ( int i = 0; i < len; i++ )
	{
		ch[i] = getByte();
	}

	return ch;
}

TQByteArray Buffer::getBBlock(WORD len)
{
	TQByteArray data;
	data.duplicate(mBuffer.data() + mReadPos, len);
	mReadPos += len;
	return data;
}


WORD *Buffer::getWordBlock(WORD len)
{
	kdDebug(14150) << k_funcinfo << "of length " << len << endl;
	WORD *ch=new WORD[len+1];
	for (unsigned int i=0; i<len; i++)
	{
		ch[i]=getWord();
	}
	ch[len]=0;
	return ch;
}


TQCString Buffer::getLEBlock(WORD len)
{
	TQCString ch;
	for (unsigned int i=0;i<len;i++)
		ch += getLEByte();
	
	return ch;
}

int Buffer::addTLV16(const WORD type, const WORD data)
{
	addWord(type);
	addWord(0x0002); //2 bytes long
	return addWord(data);
}

int Buffer::addLETLV16(const WORD type, const WORD data)
{
	addLEWord(type);
	addLEWord(0x0002); //2 bytes long
	return addLEWord(data);
}

int Buffer::addTLV8(const WORD type, const BYTE data)
{
	addWord(type);
	addWord(0x0001); //1 byte long
	return addByte(data);
}

int Buffer::addLETLV8(const WORD type, const BYTE data)
{
	addLEWord(type);
	addLEWord(0x0001); //1 byte long
	return addLEByte(data);
}

TLV Buffer::getTLV()
{
	TLV t;
	if(length() >= 4)
	{
		t.type = getWord();
		t.length = getWord();
		if ( t )
			t.data = getBlock( t.length );
		/*else
			kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Invalid TLV in buffer" << endl;*/
	}
	
	//kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "TLV data is " << t.data << endl;
	return t;
}

TQValueList<TLV> Buffer::getTLVList()
{
	TQValueList<TLV> ql;

	while (mReadPos < mBuffer.size())
	{
		TLV t;

		t = getTLV();
		if ( !t )
		{
			kdDebug(14150) << k_funcinfo << "Invalid TLV found" << endl;
			continue;
		}

		//kdDebug(14150) << k_funcinfo << "got TLV(" << t.type << ")" << endl;
		ql.append(t);
	}

	return ql;
}

int Buffer::addChatTLV(const WORD type, const WORD exchange,
	const TQString &roomname, const WORD instance)
{
	addWord(type);
	addWord(0x0005 + roomname.length());
	addWord(exchange);
	addByte(roomname.length());
	addString(roomname.latin1(), roomname.length()); // TODO: check encoding

	return addWord(instance);
}

void Buffer::expandBuffer(unsigned int inc)
{
	mBuffer.resize(mBuffer.size()+inc, TQGArray::SpeedOptim);
}

TQCString Buffer::getLNTS()
{
	WORD len = getLEWord();
	TQCString qcs;
	qcs.duplicate( getBlock(len) );
	return qcs;
}

TQCString Buffer::getLELNTS()
{
	WORD len = getLEWord();
	TQCString qcs;
	qcs.duplicate( getBlock(len) );
	return qcs;
}

int Buffer::addLNTS(const char *s)
{
	unsigned int len = strlen(s);

	addLEWord(len+1);
	if(len > 0)
		addString(s, len);
	int ret = addByte(0x00);
	return ret;
}

int Buffer::addLELNTS(const char *s)
{
	unsigned int len = strlen(s);
	int ret = addLEWord(len+1);
	if(len > 0)
		ret = addLEString(s, len);
	ret = addByte(0x00);
	return ret;
}

int Buffer::addBSTR(const char * s)
{
	unsigned int len = strlen(s);
	int ret = addWord(len);
	if(len > 0)
		ret = addString(s, len);
	return ret;
}

TQByteArray Buffer::getBSTR()
{
	WORD len = getWord();
	TQByteArray qba;
	qba.duplicate( getBlock(len) );
	return qba;
}

int Buffer::addBUIN(const char * s)
{
	unsigned int len = strlen(s);
	int ret = addByte(len);
	ret = addString(s, len);
	return ret;
}

TQByteArray Buffer::getBUIN()
{
	BYTE len = getByte();
	TQByteArray qba;
	qba.duplicate( getBlock(len) );
	return qba;
}

char *Buffer::buffer() const
{
	return const_cast<char*>(mBuffer.data());
}

int Buffer::length() const
{
	return (mBuffer.size() - mReadPos);
}

TQString Buffer::toString() const
{
	// line format:
	//00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38   |;tJ�..........|

	int i = 0;
	TQString output = "\n";
	TQString hex, ascii;

	TQByteArray::ConstIterator it;
	for ( it = mBuffer.begin(); it != mBuffer.end(); ++it )
	{
		i++;

		unsigned char c = static_cast<unsigned char>(*it);

		if ( c < 0x10 )
			hex.append("0");
		hex.append(TQString("%1 ").arg(c, 0, 16));

		ascii.append(isprint(c) ? c : '.');

		if (i == 16)
		{
			output += hex + "   |" + ascii + "|\n";
			i=0;
			hex=TQString();
			ascii=TQString();
		}
	}

	if(!hex.isEmpty())
		output += hex.leftJustify(48, ' ') + "   |" + ascii.leftJustify(16, ' ') + '|';
	output.append('\n');

	return output;
}

TQString Buffer::peekBSTR()
{
	int lastPos = mReadPos;
	TQByteArray qba = getBSTR();
	mReadPos = lastPos;
	return TQString( qba );
}
TQString Buffer::peekBUIN()
{
	int lastPos = mReadPos;
	TQByteArray qba = getBUIN();
	mReadPos = lastPos;
	return TQString( qba );
}

Buffer::operator TQByteArray() const
{
	return mBuffer;
}
