//
// DX9MeshEffect.cpp
//
// DX9Mesh Effect Plugin
//
// Copyright (c) 2000-2004 memon/moppi productions
//

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3d9.h>
#include <d3dx9math.h>
#include <dxerr9.h>

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "DX9DeviceC.h"
#include "Dx9ViewportC.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "Vector2C.h"
#include "BBox2C.h"
#include "EffectMaskI.h"

#include "DX9MeshPlugin.h"

using namespace PajaTypes;
using namespace PluginClass;
using namespace PajaSystem;
using namespace Edit;
using namespace Composition;
using namespace Import;
using namespace FileIO;

using namespace DX9MeshPlugin;

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 );
}

//////////////////////////////////////////////////////////////////////////
//
// X Importer class implementation.
//

DX9XImportC::DX9XImportC() :
	m_pModel( 0 ),
	m_bFileRefsValid( false ),
	m_pData( 0 ),
	m_ui32DataSize( 0 ),
	m_bHasTextures( false )
{
}

DX9XImportC::DX9XImportC( EditableI* pOriginal ) :
	ImportableI( pOriginal ),
	m_pModel( 0 ),
	m_bFileRefsValid( false ),
	m_pData( 0 ),
	m_ui32DataSize( 0 ),
	m_bHasTextures( false )
{
}

DX9XImportC::~DX9XImportC()
{
	// Return if this is a clone.
	if( get_original() )
		return;

	// Delete creted texture.
	if( m_pModel )
	{
		delete m_pModel;
		m_pModel = 0;
	}

	delete [] m_pData;
}

DX9XImportC*
DX9XImportC::create_new()
{
	return new DX9XImportC;
}

DataBlockI*
DX9XImportC::create()
{
	return new DX9XImportC;
}

DataBlockI*
DX9XImportC::create( EditableI* pOriginal )
{
	return new DX9XImportC( pOriginal );
}

void
DX9XImportC::copy( EditableI* pEditable )
{
	// Importables which loads the data from a file does
	// not have to implement this method, since they will
	// not be duplicated.
}

void
DX9XImportC::restore( EditableI* pEditable )
{
	DX9XImportC*	pFile = (DX9XImportC*)pEditable;

	m_pModel = pFile->m_pModel;
	m_sFileName = pFile->m_sFileName;
	m_bFileRefsValid = pFile->m_bFileRefsValid;
	m_rFileRefs = pFile->m_rFileRefs;
	m_pData = pFile->m_pData;
	m_ui32DataSize = pFile->m_ui32DataSize;
}

const char*
DX9XImportC::get_filename()
{
	return m_sFileName.c_str();
}

void
DX9XImportC::set_filename( const char* szName )
{
	m_sFileName = szName;
}

// loads the file
bool
DX9XImportC::load_file( const char* szName, DemoInterfaceC* pInterface )
{
	m_pDemoInterface = pInterface;

	DeviceContextC* pContext = m_pDemoInterface->get_device_context();

	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return false;
	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	// Delete existing data.
	delete m_pModel;
	m_pModel = 0;

	delete [] m_pData;
	m_pData = 0;
	m_ui32DataSize = 0;

	FILE*		pStream;
	if( (pStream = fopen( pInterface->get_absolute_path( szName ), "rb" )) == 0 )
	{
		return false;
	}

	m_sFileName = szName;

	// Calc file size
	fseek( pStream, 0L, SEEK_END );
	m_ui32DataSize = ftell( pStream );
	fseek( pStream, 0L, SEEK_SET );

	if( !m_ui32DataSize )
	{
		fclose( pStream );
		return false;
	}

	m_pData = new uint8[m_ui32DataSize];

	if( fread( m_pData, m_ui32DataSize, 1, pStream ) != 1 )
	{
		fclose( pStream );
		return false;
	}

	fclose( pStream );

	m_sFileName = szName;
	m_bFileRefsValid = false;
	m_bHasTextures = false;

	return true;
}

void
DX9XImportC::initialize( uint32 ui32Reason, DemoInterfaceC* pInterface )
{
	ImportableI::initialize( ui32Reason, pInterface );

	DeviceContextC* pContext = pInterface->get_device_context();
	TimeContextC* pTimeContext = pInterface->get_time_context();

	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return;
	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	if( ui32Reason == INIT_DEVICE_CHANGED )
	{

		if( pDevice->get_state() == DEVICE_STATE_SHUTTINGDOWN )
		{
			// Delete textures
			if( m_pModel )
			{
				delete m_pModel;
				m_pModel = 0;
			}
		}

	}
	else if( ui32Reason == INIT_DEVICE_INVALIDATE )
	{
		// Invalidate device resources.
	}
	else if( ui32Reason == INIT_DEVICE_VALIDATE )
	{
		// Restore device resources.
	}
	else if( ui32Reason == INIT_INITIAL_UPDATE )
	{
		if( m_pData )
		{
			m_pModel = new CD3DModel( pD3DDevice );
			HRESULT	hRes;
			
			if( FAILED( hRes = m_pModel->LoadXFile( m_pData, m_ui32DataSize ) ) )
			{
				TRACE( "Load failed: %s\n", DXGetErrorString9( hRes ) );
				return;
			}

			if( !m_bHasTextures )
			{
				TRACE( "loading texture\n" );
				// No textures yet, reload...
				m_pModel->LoadTextures( pInterface );
				m_bHasTextures = true;
			}
			else
			{
				TRACE( "fixing texture\n" );
				if( !m_vecTexHandles.empty() )
				{
					// Set textures to the model, and fixup.
					m_pModel->SetTextureCount( (int)m_vecTexHandles.size() );
					for( int32 i = 0; i < (int32)m_vecTexHandles.size(); i++ )
					{
						m_pModel->SetTextureHandle( i, m_vecTexHandles[i].pHandle );
						m_pModel->SetTextureName( i, m_vecTexHandles[i].strName );
						m_pModel->SetBumpTextureHandle( i, m_vecTexHandles[i].pBumpHandle );
						m_pModel->SetBumpTextureName( i, m_vecTexHandles[i].strBumpName );
					}
					m_pModel->FixTextures();
				}
			}
		}
		m_bFileRefsValid = false;
	}
}

SuperClassIdC
DX9XImportC::get_super_class_id()
{
	return SUPERCLASS_IMPORT;
}

ClassIdC
DX9XImportC::get_class_id()
{
	return CLASS_DX9X_IMPORT;
}

const char*
DX9XImportC::get_class_name()
{
	return CLASS_DX9X_IMPORT_NAME;
}

const char*
DX9XImportC::get_info()
{
	static char	szInfo[256] = "";

	if( m_pModel )
	{
		D3DXVECTOR3	vCen = m_pModel->GetBoundingSphereCenter();
		float32			f32Rad = m_pModel->GetBoundingSphereRadius();
		_snprintf( szInfo, 255, "C: (%f, %f, %f) R:%f", vCen.x, vCen.y, vCen.z, f32Rad );
	}
	else
	{
		_snprintf( szInfo, 255, "<pah>" );
	}
	return szInfo;
}

ClassIdC
DX9XImportC::get_default_effect()
{
	return CLASS_DX9MESH_EFFECT;
}


int32
DX9XImportC::get_duration()
{
	return -1;
}

float32
DX9XImportC::get_start_label()
{
	return 0;
}

float32
DX9XImportC::get_end_label()
{
	return 0;
}

void
DX9XImportC::eval_state( int32 i32Time )
{
	// empty
}

uint32
DX9XImportC::get_reference_file_count()
{
	if( !m_bFileRefsValid )
	{
		TRACE( "updating refs\n" );
		m_rFileRefs.clear();
		if( m_pModel )
		{
			TRACE( " - %d textures\n", m_pModel->GetTextureCount() );
			for( int32 i = 0; i < m_pModel->GetTextureCount(); i++ )
			{
				if( m_pModel->GetTextureHandle( i ) )
					m_rFileRefs.push_back( m_pModel->GetTextureHandle( i ) );
				if( m_pModel->GetBumpTextureHandle( i ) )
					m_rFileRefs.push_back( m_pModel->GetBumpTextureHandle( i ) );
			}
		}
		m_bFileRefsValid = true;
	}
	else
		TRACE( "refs ok (%d)\n", m_rFileRefs.size() );

	return (uint32)m_rFileRefs.size();
}

FileHandleC*
DX9XImportC::get_reference_file( uint32 ui32Index )
{
	if( !m_bFileRefsValid )
	{
		m_rFileRefs.clear();
		if( m_pModel )
		{
			for( int32 i = 0; i < m_pModel->GetTextureCount(); i++ )
			{
				if( m_pModel->GetTextureHandle( i ) )
					m_rFileRefs.push_back( m_pModel->GetTextureHandle( i ) );
				if( m_pModel->GetBumpTextureHandle( i ) )
					m_rFileRefs.push_back( m_pModel->GetBumpTextureHandle( i ) );
			}
		}
		m_bFileRefsValid = true;
	}

	if( ui32Index >= 0 && ui32Index < m_rFileRefs.size() )
		return m_rFileRefs[ui32Index];

	return 0;
}

CD3DModel*
DX9XImportC::get_model()
{
	return m_pModel;
}

enum DX9XImportChunksE {
	CHUNK_XIMPORT_BASE =	0x1000,
	CHUNK_XIMPORT_DATA =	0x2000,
};

const uint32	XIMPORT_VERSION = 1;

uint32
DX9XImportC::save( SaveC* pSave )
{
	uint32		ui32Error = IO_OK;
	std::string	sStr;
	uint32	ui32Temp;

	// file base
	pSave->begin_chunk( CHUNK_XIMPORT_BASE, XIMPORT_VERSION );
		sStr = m_sFileName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );
	pSave->end_chunk();

	if( m_pModel )
	{
		// file data
		uint32	ui32TexSize = 0;
		pSave->begin_chunk( CHUNK_XIMPORT_DATA, XIMPORT_VERSION );
			// Write the loaded texture names.
			ui32TexSize = m_pModel->GetTextureCount();
			ui32Error = pSave->write( &ui32TexSize, sizeof( ui32TexSize ) );
			for( int32 i = 0; i < m_pModel->GetTextureCount(); i++ )
			{
				// Write handle.
				FileHandleC*	pHandle = m_pModel->GetTextureHandle( i );
				ui32Temp = 0xffffffff;
				if( pHandle )
					ui32Temp = pHandle->get_id();
				ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

				// Write name.
				sStr = m_pModel->GetTextureName( i );
				if( sStr.size() > 255 )
					sStr.resize( 255 );
				ui32Error = pSave->write_str( sStr.c_str() );

				// Write bump handle.
				FileHandleC*	pBumpHandle = m_pModel->GetBumpTextureHandle( i );
				ui32Temp = 0xffffffff;
				if( pBumpHandle )
					ui32Temp = pBumpHandle->get_id();
				ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

				// Write bump name.
				sStr = m_pModel->GetBumpTextureName( i );
				if( sStr.size() > 255 )
					sStr.resize( 255 );
				ui32Error = pSave->write_str( sStr.c_str() );
			}

			// Write mesh data.
			ui32Error = pSave->write( &m_ui32DataSize, sizeof( m_ui32DataSize ) );
			ui32Error = pSave->write( m_pData, m_ui32DataSize );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
DX9XImportC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	char	szStr[256];

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_XIMPORT_BASE:
			{
				if( pLoad->get_chunk_version() <= XIMPORT_VERSION ) {
					ui32Error = pLoad->read_str( szStr );
					m_sFileName = szStr;
				}
			}
			break;

		case CHUNK_XIMPORT_DATA:
			{
				if( pLoad->get_chunk_version() <= XIMPORT_VERSION )
				{
					// Delete existing data.
					delete [] m_pData;
					if( m_pModel )
						delete m_pModel;
					m_pModel = 0;

					// Read texture count.
					uint32	ui32TexCount = 0;
					ui32Error = pLoad->read( &ui32TexCount, sizeof( ui32TexCount ) );

					m_vecTexHandles.resize( ui32TexCount );
					for( uint32 i = 0; i < ui32TexCount; i++ )
					{
						// texture handle
						m_vecTexHandles[i].pHandle = 0;
						ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
						if( ui32Temp != 0xffffffff )
							pLoad->add_file_handle_patch( (void**)&m_vecTexHandles[i].pHandle, ui32Temp );
						// Name
						ui32Error = pLoad->read_str( szStr );
						m_vecTexHandles[i].strName = szStr;
						// bump texture handle
						m_vecTexHandles[i].pBumpHandle = 0;
						ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
						if( ui32Temp != 0xffffffff )
							pLoad->add_file_handle_patch( (void**)&m_vecTexHandles[i].pBumpHandle, ui32Temp );
						// bump Name
						ui32Error = pLoad->read_str( szStr );
						m_vecTexHandles[i].strBumpName = szStr;
					}

					// Read data
					ui32Error = pLoad->read( &m_ui32DataSize, sizeof( m_ui32DataSize ) );

					m_pData = new uint8[m_ui32DataSize];

					ui32Error = pLoad->read( m_pData, m_ui32DataSize );
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	m_bHasTextures = true;

	return IO_OK;
}
