/***************************************************************************
                          cbytearray.cpp  -  description
                             -------------------
    begin                : Fri Mar 22 2002
    copyright            : (C) 2002-2004 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "cbytearray.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cfile.h"
#include "cdir.h"

/** */
CByteArray::CByteArray( unsigned long nSize )
{
	m_pchBuffer = NULL;

	// clear allready called by SetSize
	SetSize(nSize);
}

/** */
CByteArray::~CByteArray()
{
	Clear();
}

/** */
void CByteArray::Clear()
{
	if (m_pchBuffer)
	{
		free(m_pchBuffer);
		m_pchBuffer = NULL;	
	}

	m_nSize       = 0;
	m_nBufferSize = 0;
}

/** */
void CByteArray::SetSize( unsigned long nNewSize )
{
	Clear();

	// sanity check	
	if( nNewSize > 0 )
	{
		m_pchBuffer = (unsigned char*)malloc(nNewSize);

		if ( m_pchBuffer == NULL )
		{
			perror("CByteArray::SetSize");
		}
		else
		{
			m_nSize = m_nBufferSize = nNewSize;
		}
	}
}

/** */
bool CByteArray::Realloc( unsigned long newsize )
{
	/*
	 * realloc() can be called with a NULL pointer or with size 0
	 * But with size 0 you cannot tell if it succeeded or not.
	 */
	if ( newsize == 0 )
	{
		Clear();
		return true;
	}
	else
	{
		void * p = realloc( m_pchBuffer, newsize );
		
		if ( p == NULL )
		{
			perror("CByteArray::Realloc");
			return false;
		}
		else
		{
			m_pchBuffer = (unsigned char*) p;
			m_nSize = newsize;
			m_nBufferSize = newsize;
			return true;
		}
	}
}

/** */
void CByteArray::Append( const unsigned char * pchSrc, unsigned long nSize )
{
	unsigned char *p;
	unsigned char * temp;

	// sanity check
	if ( (nSize == 0) || (pchSrc == NULL) )
	{
		return;
	}

	// check for overlapping memory areas
	if ( (pchSrc >= m_pchBuffer) && (pchSrc <= m_pchBuffer+m_nBufferSize) )
	{
		temp = (unsigned char*) malloc( nSize );
		if ( temp == NULL )
		{
			perror("CByteArray::Append malloc");
			return;
		}
		memcpy(temp,pchSrc,nSize);
		
	}
	else
	{
		temp = (unsigned char*) pchSrc;
	}

	if ( (m_nSize+nSize) > m_nBufferSize )
	{
		// resize buffer
		m_nBufferSize += nSize + 10000;
		p = (unsigned char*) realloc(m_pchBuffer,m_nBufferSize);

		if ( p == NULL )
		{
			m_nBufferSize -= (nSize + 10000);
			perror("CByteArray::Append realloc");
			
			if ( temp != pchSrc )
			{
				free(temp);
			}
			
			return;
		}

		m_pchBuffer = p;
	}

	// copy mem
	memcpy( m_pchBuffer+m_nSize, temp, nSize );
	// update array size
	m_nSize += nSize;
	
	if ( temp != pchSrc )
	{
		free(temp);
	}
}

/** */
bool CByteArray::LoadFromFile( CString file )
{
	CFile f;
	CDir d;
	ulonglong filesize;
	bool res = false;

	filesize = d.getFileSize(file,false);

	if ( f.Open( file, IO_RAW | IO_READONLY ) )
	{
		SetSize(filesize);

		if ( f.Read( (char*)Data(), Size() ) == filesize )
		{
			res = true;
		}

		f.Close();
	}
	
	return res;
}

/** */
bool CByteArray::SaveToFile( CString file )
{
	CFile f;
	bool res = false;
	CString tempname = file;

	if ( f.OpenTemp( tempname ) )
	{
		if ( (Size() == 0) || (f.Write( (const char*)Data(), Size() ) == Size()) )
		{
			if ( f.Close() )
			{
				/* ignore unlink error, may not already exist */
				CFile::UnLink( file );
				
				res = CFile::Rename( tempname, file );
			}
		}
	}
	
	return res;
}
