//-------------------------------------------------------------------------
//
// File:		LoadMemC.cpp
// Desc:		Input stream class implementation.
// Author:		memon <memon@inside.org>
//
//-------------------------------------------------------------------------
//	Copyright (c) 2000 Moppi Productions. All Rights Reserved.
//  This file is part of Moppi Demopaja SDK. For conditions of 
//  distribution and use, see the accompanying license.txt file.
//  http://moppi.inside.org/demopaja/
//-------------------------------------------------------------------------


#include <windows.h>

#include "LoadMemC.h"
#include "PajaTypes.h"
#include "FactoryC.h"
#include "FileIO.h"
#include <stdio.h>
#include <vector>
#include <string>


static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}

using namespace PajaTypes;
using namespace FileIO;
using namespace PluginClass;


LoadMemC::LoadMemC( FactoryC* pFactory ) :
	LoadC( pFactory ),
	m_pFactory( pFactory ),
	m_ui32Indent( 0 ),
	m_ui32Error( IO_OK ),
	m_pData( 0 ),
	m_ui32DataSize( 0 ),
	m_i32FilePos( 0 )
{
	// empty
}

LoadMemC::~LoadMemC()
{
	close();
}

int32
LoadMemC::read_mem( void* pBuffer, int32 i32Size )
{
	if( !m_pData )
		return 0;

	if( (m_i32FilePos + i32Size) > (int32)m_ui32DataSize ) {
		i32Size = (int32)m_ui32DataSize - m_i32FilePos;
		memcpy( pBuffer, &m_pData[m_i32FilePos], i32Size );
		m_i32FilePos += i32Size;
		return i32Size;
	}

	memcpy( pBuffer, &m_pData[m_i32FilePos], i32Size );
	m_i32FilePos += i32Size;

	return i32Size;
}

int32
LoadMemC::seek_mem( int32 i32Offset, MemSeekE eOrigin )
{
	if( !m_pData )
		return 0;

	switch( eOrigin ) {
	case MEM_SEEK_SET:
		m_i32FilePos = i32Offset;

		if( m_i32FilePos < 0 )
			m_i32FilePos = 0;
		else if( m_i32FilePos >= (int32)m_ui32DataSize )
			m_i32FilePos = (int32)m_ui32DataSize - 1;

		break;
	case MEM_SEEK_END:
		m_i32FilePos = (int32)m_ui32DataSize - 1 + i32Offset;

		if( m_i32FilePos < 0 )
			m_i32FilePos = 0;
		else if( m_i32FilePos >= (int32)m_ui32DataSize )
			m_i32FilePos = (int32)m_ui32DataSize - 1;
		break;
	case MEM_SEEK_CUR:
		m_i32FilePos += i32Offset;

		if( m_i32FilePos < 0 )
			m_i32FilePos = 0;
		else if( m_i32FilePos >= (int32)m_ui32DataSize )
			m_i32FilePos = (int32)m_ui32DataSize - 1;

		break;
	}

	return 1;
}

bool
LoadMemC::eof_mem()
{
	if( m_i32FilePos >= ((int32)m_ui32DataSize - 1) )
		return true;
	return false;
}


int32
LoadMemC::tell_mem()
{
	return m_i32FilePos;
}


uint32
LoadMemC::open( uint8* pData, uint32 ui32DataSize, const int8* pSignature, int32 i32SignatureSize )
{
	// set pointers
	m_pData = pData;
	m_ui32DataSize = ui32DataSize;


	// read file signature
	int8*	pReadSig = new int8[i32SignatureSize];
	read_mem( pReadSig, i32SignatureSize );	// read null ternimated string
	if( memcmp( pSignature, pReadSig, i32SignatureSize ) != 0 ) {
		delete pReadSig;

		close();

		return IO_ERROR_FORMAT;
	}
	delete pReadSig;

	uint32	ui32CurPos = tell_mem();
	seek_mem( 0, MEM_SEEK_END );
	m_ui32FileSize = tell_mem();
	seek_mem( ui32CurPos, MEM_SEEK_SET );

	return IO_OK;
}

uint32
LoadMemC::close()
{
	m_pData = 0;
	m_ui32DataSize = 0;
	m_i32FilePos = 0;
	return IO_OK;
}

uint32
LoadMemC::open_chunk()
{
	if( m_ui32Error )
		return m_ui32Error;

	if( /*!m_pStream ||*/ m_ui32Error != IO_OK )
		return IO_ERROR_OPEN;

	if( m_ui32Indent && (tell_mem() >= m_rChunkList[m_ui32Indent - 1].m_ui32Next) )
		return IO_END;

	if( eof_mem() )
		return IO_EOF;

	ChunkHdrS	rHdr;
	if( read_mem( &rHdr.m_ui32ID, sizeof( rHdr.m_ui32ID ) ) != sizeof( rHdr.m_ui32ID ) )
		return IO_ERROR_READ;
	if( read_mem( &rHdr.m_ui32Version, sizeof( rHdr.m_ui32Version ) ) != sizeof( rHdr.m_ui32Version ) )
		return IO_ERROR_READ;
	if( read_mem( &rHdr.m_ui32Size, sizeof( rHdr.m_ui32Size ) ) != sizeof( rHdr.m_ui32Size ) )
		return IO_ERROR_READ;

	rHdr.m_ui32Size -= 12;	// subscract the header size.
	rHdr.m_ui32Next = tell_mem() + rHdr.m_ui32Size;
	rHdr.m_bLastChunk = false;

	m_rChunkList.push_back( rHdr );

	m_ui32Indent++;

	return IO_OK;
}

uint32
LoadMemC::get_chunk_id()
{
	if( !m_ui32Indent )
		return 0;
	return m_rChunkList[m_ui32Indent - 1].m_ui32ID;
}

uint32
LoadMemC::get_chunk_size()
{
	if( !m_ui32Indent )
		return 0;
	return m_rChunkList[m_ui32Indent - 1].m_ui32Size;
}

uint32
LoadMemC::get_chunk_version()
{
	if( !m_ui32Indent )
		return 0;
	return m_rChunkList[m_ui32Indent - 1].m_ui32Version;
}

uint32
LoadMemC::get_chunk_indent()
{
	return m_ui32Indent;
}

uint32
LoadMemC::peek_next_chunk()
{
	if( m_ui32Error )
		return m_ui32Error;
	if( /*!m_pStream ||*/ !m_ui32Indent )
		return IO_ERROR_OPEN;

	seek_mem( m_rChunkList[m_ui32Indent - 1].m_ui32Next, MEM_SEEK_SET );
	m_rChunkList.erase( m_rChunkList.begin() + (m_ui32Indent - 1) );
	m_ui32Indent--;
	return IO_OK;
}

uint32
LoadMemC::close_chunk()
{
	if( m_ui32Error )
		return m_ui32Error;
	if( /*!m_pStream ||*/ !m_ui32Indent )
		return IO_ERROR_OPEN;

	peek_next_chunk();

	// is this last chunk in the file?
	if( tell_mem() >= m_ui32FileSize ) {
		m_ui32Error = IO_END;
		return IO_OK;
	}

	return IO_OK;
}

uint32
LoadMemC::read( void* pBuffer, uint32 ui32Size )
{
	if( m_ui32Error )
		return m_ui32Error;
	if( /*!m_pStream ||*/ !m_ui32Indent )
		return IO_ERROR_OPEN;

	uint32	ui32ReadSize = read_mem( pBuffer, ui32Size );
	if( ui32ReadSize != ui32Size ) {
		m_ui32Error = IO_ERROR_READ;
		return m_ui32Error;
	}

	return IO_OK;
}

uint32
LoadMemC::read_str( char* szStr )
{
	if( m_ui32Error )
		return m_ui32Error;
	if( /*!m_pStream ||*/ !m_ui32Indent )
		return IO_ERROR_OPEN;

	// first read size of the string
	uint16	ui16StrSize;

	if( read_mem( &ui16StrSize, sizeof( ui16StrSize ) ) != sizeof( ui16StrSize ) ) {
		m_ui32Error = IO_ERROR_READ;
		return m_ui32Error;
	}

	if( ui16StrSize ) {
		uint32	ui32ReadSize = read_mem( szStr, ui16StrSize );
		if( ui32ReadSize != ui16StrSize ) {
			m_ui32Error = IO_ERROR_READ;
			return m_ui32Error;
		}
	}

	// terminate the string
	szStr[ui16StrSize] = '\0';

	return IO_OK;
}

uint32
LoadMemC::get_error()
{
	return m_ui32Error;
}

void
LoadMemC::add_file_handle_patch( void** pPointer, uint32 ui32HandleId )
{
	FileHandlePatchS	rPatch;
	rPatch.m_pPointer = pPointer;
	rPatch.m_ui32HandleId = ui32HandleId;
	m_rFileHandlePatches.push_back( rPatch );
}

uint32
LoadMemC::get_file_handle_patch_count()
{
	return m_rFileHandlePatches.size();
}

void**
LoadMemC::get_file_handle_patch_pointer( uint32 ui32Index )
{
	return m_rFileHandlePatches[ui32Index].m_pPointer;
}

uint32
LoadMemC::get_file_handle_patch_id( PajaTypes::uint32 ui32Index )
{
	return m_rFileHandlePatches[ui32Index].m_ui32HandleId;
}

FactoryC*
LoadMemC::get_factory() const
{
	return m_pFactory;
}

uint32
LoadMemC::get_error_message_count()
{
	return m_rErrorLog.size();
}

const char*
LoadMemC::get_error_message( PajaTypes::uint32 ui32Index )
{
	return m_rErrorLog[ui32Index].m_sMessage.c_str();
}

void
LoadMemC::add_error_message( const char* szMessage )
{
	ErrorMessageS	rErrMsg;
	rErrMsg.m_sMessage = szMessage;
	m_rErrorLog.push_back( rErrMsg );
}
