//
// This file is part of the aMule Project.
//
// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// 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 St, Fifth Floor, Boston, MA  02110-1301, USA
//

#include "MemFile.h"	// Interface declarations


CMemFile::CMemFile(unsigned int growthRate)
{
	m_buffer		= NULL;
	m_BufferSize	= 0;
	m_fileSize		= 0;
	m_growthRate	= growthRate;
	m_position		= 0;
	m_delete		= true;
	m_readonly		= false;
}


CMemFile::CMemFile(byte* buffer, size_t bufferSize)
{
	MULE_VALIDATE_PARAMS(buffer, wxT("CMemFile: Attempted to attach invalid buffer."));

	m_buffer		= buffer;
	m_BufferSize	= bufferSize;
	m_fileSize		= bufferSize;
	m_growthRate	= 0;
	m_position		= 0;
	m_delete		= false;
	m_readonly		= false;
}

CMemFile::CMemFile(const byte* buffer, size_t bufferSize)
{
	MULE_VALIDATE_PARAMS(buffer, wxT("CMemFile: Attempted to attach invalid buffer."));

	m_buffer		= const_cast<byte*>(buffer);
	m_BufferSize	= bufferSize;
	m_fileSize		= bufferSize;
	m_growthRate	= 0;
	m_position		= 0;
	m_delete		= false;
	m_readonly		= true;
}

CMemFile::~CMemFile()
{
	if (m_delete) {
		free(m_buffer);
	}
}


uint64 CMemFile::GetPosition() const
{
	return m_position;
}


void CMemFile::SetLength(size_t newLen)
{
	MULE_VALIDATE_STATE(!m_readonly, wxT("CMemFile: Attempted to change length on a read-only buffer."));

	if (newLen > m_BufferSize) {
		enlargeBuffer(newLen);
	}

	if (newLen < m_position) {
		m_position = newLen;
	}

	m_fileSize = newLen;
}


uint64 CMemFile::GetLength() const
{
	return m_fileSize;
}


void CMemFile::enlargeBuffer(size_t size)
{
	MULE_VALIDATE_PARAMS(size >= m_BufferSize, wxT("CMemFile: Attempted to shrink buffer."));
	MULE_VALIDATE_STATE(m_delete, wxT("CMemFile: Attempted to grow an attached buffer."));
	MULE_VALIDATE_STATE(!m_readonly, wxT("CMemFile: Attempted to grow a read-only buffer."));

	size_t newsize = m_BufferSize;

	if (m_growthRate) {
		newsize = ((size + m_growthRate - 1) / m_growthRate) * m_growthRate;
	} else {
		// No growth-rate specified. Change to exactly the size specified.
		newsize = size;
	}

	byte *tmp = (byte*)realloc(m_buffer, newsize);
	if (tmp) {
		m_buffer = tmp;
		m_BufferSize = newsize;
	}

	MULE_VALIDATE_STATE(tmp, wxT("CMemFile: Failed to (re)allocate buffer"));
}


sint64 CMemFile::doRead(void* buffer, size_t count) const
{
	MULE_VALIDATE_PARAMS(buffer, wxT("CMemFile: Attempting to read to invalid buffer"));

	// Handle reads past EOF
	if (m_position > m_fileSize) {
		return 0;
	} else if (m_position + count > m_fileSize) {
		count = m_fileSize - m_position;
	}

	if (count) {
		memcpy(buffer, m_buffer + m_position, count);
		m_position += count;
	}

	return count;
}


sint64 CMemFile::doWrite(const void* buffer, size_t count)
{
	MULE_VALIDATE_PARAMS(buffer, wxT("CMemFile: Attempting to write to invalid buffer"));
	MULE_VALIDATE_STATE(!m_readonly, wxT("CMemFile: Attempted to write to a read-only buffer."));

	// Needs more space?
	if (m_position + count > m_BufferSize) {
		enlargeBuffer(m_position + count);
	}

	MULE_VALIDATE_STATE(m_position + count <= m_BufferSize, wxT("CMemFile: Buffer not resized to needed size."));

	memcpy(m_buffer + m_position, buffer, count);
	m_position += count;

	if (m_position > m_fileSize) {
		m_fileSize = m_position;
	}

	return count;
}


sint64 CMemFile::doSeek(sint64 offset) const
{
	MULE_VALIDATE_PARAMS(offset >= 0, wxT("CMemFile: Invalid seek, position, must be positive."));

	return m_position = offset;
}


void CMemFile::ResetData()
{
	wxCHECK_RET(!m_readonly, wxT("Trying to reset read-only buffer"));

	memset(m_buffer, 0, m_BufferSize);
	m_fileSize	= 0;
	m_position	= 0;
}

// File_checked_for_headers
