#include "Mix/Private/IO/BufferedReader.h"
#include "Mix/Memory/IBuffer.h"

namespace Mix{ namespace IO{

BufferedReader* BufferedReader::CreateInstance( const wchar_t* filePath, Mix::Memory::IBuffer* pBuffer )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GENERAL, BufferedReader, filePath, pBuffer );
}

BufferedReader::BufferedReader( const wchar_t* filePath, Mix::Memory::IBuffer* pBuffer ) :
m_FilePath( filePath ),
m_pBuffer( NULL ),
m_pBegin( NULL ),
m_Size( 0 ),
m_Pos( 0 )
{
	MIX_ASSERT( pBuffer != NULL );

	MIX_ADD_REF( pBuffer );
	m_pBuffer = pBuffer;

	m_pBegin = static_cast<const UInt8*>( m_pBuffer->GetConstPointer() );

	m_Size = m_pBuffer->GetSize();
	m_Pos = 0;
}

BufferedReader::~BufferedReader( void )
{
	MIX_RELEASE( m_pBuffer );
}

UInt32 BufferedReader::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 BufferedReader::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 BufferedReader::GetPos( void ) const
{
	return m_Pos;
}

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

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

Boolean BufferedReader::Clone( Mix::IO::IReader** ppReader )
{
	Mix::IO::BufferedReader* pReader;

	pReader = Mix::IO::BufferedReader::CreateInstance( m_FilePath.GetConstPtr(), m_pBuffer );
	if( pReader != NULL )
	{
		MIX_ADD_REF( m_pBuffer );
	}
	else
	{
		return False;
	}

	( *ppReader ) = pReader;

	return True;
}

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

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

}}
