#include "Mix/Private/IO/MemoryReader.h"

namespace Mix{ namespace IO{

MemoryReader* MemoryReader::CreateInstance( const wchar_t* filePath, const void* pMem, UInt64 memSize )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GENERAL, MemoryReader, filePath, pMem, memSize );
}

MemoryReader::MemoryReader( const wchar_t* filePath, const void* pMem, UInt64 memSize ) :
m_FilePath( L"" ),
m_pMem( NULL ),
m_pBegin( NULL ),
m_Size( memSize ),
m_Pos( 0 )
{
	MIX_ASSERT( filePath != NULL );
	MIX_ASSERT( pMem != NULL );
	MIX_ASSERT( memSize > 0 );

	m_FilePath = filePath;
	m_pMem = reinterpret_cast<const UInt8*>( pMem );
	m_pBegin = m_pMem;
	m_Pos = 0;
	m_Size = memSize;
}

MemoryReader::~MemoryReader( void )
{
}

UInt32 MemoryReader::Lock( const void** ppReadBuffer, UInt32 readSize )
{
	if( ( readSize == 0 ) || ( m_Pos >= m_Size ) )
	{
		return 0;
	}

	UInt32 size;

	if( ( m_Pos + readSize ) >= m_Size )
	{
		size = static_cast<UInt32>( m_Size - m_Pos );
	}
	else
	{
		size = readSize;
	}

	( *ppReadBuffer ) = m_pBegin + m_Pos;

	m_Pos += size;

	return size;
}

UInt32 MemoryReader::Read( void* pReadBuffer, UInt32 readSize )
{
	if( ( pReadBuffer == NULL ) ||
		( readSize == 0 ) ||
		( m_Pos >= m_Size ) )
	{
		return 0;
	}

	UInt32 size;

	if( ( m_Pos + readSize ) >= m_Size )
	{
		size = static_cast<UInt32>( m_Size - m_Pos );
	}
	else
	{
		size = readSize;
	}

	Mix::Memory::Copy( pReadBuffer, ( m_pBegin + m_Pos ), size );

	m_Pos += size;

	return size;
}

UInt64 MemoryReader::Seek( Mix::IO::SEEK_METHOD seekMethod, Int64 offset )
{
	UInt32 modifyOffset;

	switch( seekMethod )
	{
	case Mix::IO::SEEK_METHOD_BEGIN:
		if( offset > 0 )
		{
			if( offset > 0x00000000FFFFFFFF )
			{
				m_Pos = m_Size;
			}
			else
			{
				modifyOffset = static_cast<UInt32>( offset );
				if( m_Size > modifyOffset )
				{
					m_Pos = modifyOffset;
				}
				else
				{
					m_Pos = m_Size;
				}
			}
		}
		else
		{
			m_Pos = 0;
		}
		break;

	case Mix::IO::SEEK_METHOD_CURRENT:
		if( offset < 0 )
		{
			if( -offset > 0x00000000FFFFFFFF )
			{
				m_Pos = 0;
			}
			else
			{
				modifyOffset = static_cast<UInt32>( -offset );
				if( m_Pos > modifyOffset )
				{
					m_Pos -= modifyOffset;
				}
				else
				{
					m_Pos = 0;
				}
			}
		}
		else if( offset > 0 )
		{
			if( offset > 0x00000000FFFFFFFF )
			{
				m_Pos = m_Size;
			}
			else
			{
				modifyOffset = static_cast<UInt32>( offset );
				if( ( m_Size - m_Pos ) > modifyOffset )
				{
					m_Pos += modifyOffset;
				}
				else
				{
					m_Pos = m_Size;
				}
			}
		}
		break;

	case Mix::IO::SEEK_METHOD_END:
		if( offset < 0 )
		{
			if( -offset > 0x00000000FFFFFFFF )
			{
				m_Pos = 0;
			}
			else
			{
				modifyOffset = static_cast<UInt32>( -offset );
				if( m_Size > modifyOffset )
				{
					m_Pos = ( m_Size - modifyOffset );
				}
				else
				{
					m_Pos = 0;
				}
			}
		}
		else
		{
			m_Pos = m_Size;
		}
		break;
	}

	return m_Pos;
}

UInt64 MemoryReader::GetPos( void ) const
{
	return m_Pos;
}

UInt64 MemoryReader::GetSize( void ) const
{
	return m_Size;
}

const wchar_t* MemoryReader::GetFilePath( void ) const
{
	return m_FilePath.GetConstPtr();
}

Boolean MemoryReader::IsBuffered( void ) const
{
	return False;
}

Boolean MemoryReader::Clone( Mix::IO::IReader** ppReader )
{
	return False;
}

Mix::IO::IStream::ACCESS_TYPE MemoryReader::GetAccessType( void ) const
{
	return Mix::IO::IStream::A_READ;
}

Mix::IO::IStream::SOURCE_TYPE MemoryReader::GetSourceType( void ) const
{
	return Mix::IO::IStream::S_BUFFER;
}

}}
