#include "directgem.h"
#include "enum.h"
#include "gemmath.h"
#include "loader.h"

VOID gem_Scene::SetFlags( DWORD flags )
{
	dwFlags = flags;
}

VOID gem_Scene::SetLightSize( FLOAT size )
{
	fLitSize = size;
}

gem_Scene::gem_Scene()
{
	lpd3dDevice = NULL;
	dwFlags = GEMFLAGS_SOFTWAREGEOMETRY | GEMFLAGS_HIERARCHYUPDATE | GEMFLAGS_RENDERLIGHT;
	farZ = 10000.0f;
	nearZ = 1.0f;
	aspect = 0.75f;	
	lpddsLightFlare = NULL;
}

gem_Scene::~gem_Scene()
{
}

//set viewport values for scene
VOID gem_Scene::SetViewport( FLOAT _nearZ, FLOAT _farZ, FLOAT _aspect )
{
	gem_Object*					obj;

	//set viewport values (the caller application must set viewport for device)
	farZ = _farZ;
	nearZ = _nearZ;
	aspect = _aspect;
	
	//reinit all camera object
	for( DWORD i = 0 ; obj = ObjectList.GetItem(i) ; i++ )
	{
		if( obj->Type() == GEMOBJECTTYPE_CAMERA )
			obj->Init();
	}
}

DWORD gem_Scene::GetStartFrame()
{
	return dwStartFrame;
}

DWORD gem_Scene::GetEndFrame()
{
	return dwEndFrame;
}

//init object, adding light to device and seting cameras properties
VOID gem_Scene::Init( LPDIRECT3DDEVICE7 device )
{
	gem_Object*					obj;	

	lpd3dDevice = device;	

	for( DWORD i = 0 ; obj = ObjectList.GetItem(i) ; i++ )
	{
		obj->Init();
	}

}

VOID gem_Scene::Close()
{
	for( DWORD i = 0 ; i<dwLightNumber ; i++ )
	{
		lpd3dDevice->LightEnable( i, FALSE );
	}
}

VOID gem_Scene::SetActiveCamera( TCHAR* name )
{
	gem_Object*					obj = FindObject( name );

	if( !obj )
		return;

	if( obj->Type() == GEMOBJECTTYPE_CAMERA )
	{
		currentCam = (gem_Camera*)obj;
	}
}

VOID gem_Scene::SetLightFlare( TCHAR* name )
{
	lpddsLightFlare = GetTexture( name );
}

VOID gem_Scene::SetRenderStatus( DWORD flags )
{
	dwFlags = flags;
}

VOID gem_Scene::RenderFrame( FLOAT frame )
{
	gem_Object*					obj;
	DWORD						i;

	Transform( frame );		
	
	currentCam->SetGeometry( lpd3dDevice );

	lpd3dDevice->BeginScene();

	for( i = 0 ; obj=ObjectList.GetItem(i) ; i++ )
	{
		obj->Render();
	}

	lpd3dDevice->EndScene();
}

VOID gem_Scene::Transform( FLOAT frame )
{
	gem_Keyframer*				keyframer;
	DWORD						i;	

	for( i = 0 ; keyframer = KeyframerList.GetItem(i) ; i++ )
	{
		keyframer->UpdateTransform( frame );
	}	

	for( i = 0 ; keyframer = KeyframerList.GetItem(i) ; i++ )
	{
		keyframer->ApplyTransform();
	}
}

VOID gem_Scene::Render()
{
	gem_Object*					obj;
	DWORD						i;	
	
	currentCam->SetGeometry( lpd3dDevice );

	lpd3dDevice->BeginScene();

	for( i = 0 ; obj=ObjectList.GetItem(i) ; i++ )
	{
		obj->Render();
	}

	lpd3dDevice->EndScene();
}

gem_Object* gem_Scene::FindObject( TCHAR* name )
{
	return ObjectList.FindByName( name );
}

gem_Keyframer* gem_Scene::FindKeyframer( TCHAR* name )
{
	return KeyframerList.FindByName( name );
}

gem_Material* gem_Scene::FindMaterial( TCHAR* name )
{
	return MaterialList.FindByName( name );
}

DWORD gem_Scene::ReadFramesChunk( FILE* inFile )
{

	ReadDword( inFile );
	
	dwStartFrame = ReadDword( inFile );
	dwEndFrame = ReadDword( inFile );

	if( feof( inFile ) )
		return GEMERROR_LOAD;

	return GEMERROR_OK;
}

DWORD gem_Scene::ReadKeyframerChunk( FILE *inFile )
{
	LONG						size;
	LONG						pos;
	WORD						sign;
	
	
	size = ReadDword( inFile ) - IDLENGTH;
	pos = ftell( inFile );

	while( ( !feof(inFile ) ) && ftell( inFile )<size+pos )
	{
		sign = ReadWord( inFile );

		switch (sign)
		{
			case GEMCHUNK_MESHKEYFRAMER:
			{
				gem_MeshKeyframer* mkeyframer = new gem_MeshKeyframer( this );
				if( mkeyframer->Load( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			}
			break;

			case GEMCHUNK_LIGHTKEYFRAMER:
			{
				gem_LightKeyframer* lkeyframer = new gem_LightKeyframer( this );
				if( lkeyframer->Load( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;								
			}
			break;

			case GEMCHUNK_CAMERAKEYFRAMER:
			{
				gem_CameraKeyframer* ckeyframer = new gem_CameraKeyframer( this );
				if( ckeyframer->Load( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			}
			break;

			case GEMCHUNK_CAMERATRGKEYFRAMER:
			{
				gem_CameraTrgKeyframer* ctkeyframer = new gem_CameraTrgKeyframer( this );
				if( ctkeyframer->Load( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			}
			break;

			case GEMCHUNK_FRAMES:
				if( ReadFramesChunk( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			break;

			default:
				if( JumpThroughtChunk( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			break;
		}
	}

	if( feof( inFile ) )
		return GEMERROR_LOAD;

	return GEMERROR_OK;
}


DWORD gem_Scene::ReadObjectChunk( FILE* inFile )
{
	LONG			size; //rozmiar chunka
	LONG			pos;
	WORD			sign;
	TCHAR			name[80];	
		
	size = ReadDword( inFile ) - IDLENGTH;
	pos = ftell( inFile );

	ReadASCIIZ( inFile, name );

	while( ( !feof( inFile ) ) && ftell( inFile )<size+pos )
	{
		sign = ReadWord( inFile );

		switch( sign ) 
		{
			case GEMCHUNK_TRIMESH:
			{
				gem_Mesh*	mesh = new gem_Mesh( this );
				if( mesh->Load( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
				ObjectList.AddItem( mesh, name );					
			}
			break;

			case GEMCHUNK_LIGHT:
			{
				gem_Light*	light = new gem_Light( this );
				if( light->Load( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
				ObjectList.AddItem( light, name );					
			}
			break;

			case GEMCHUNK_CAMERA:
			{
				gem_Camera*	camera = new gem_Camera( this );
				if( camera->Load( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
				ObjectList.AddItem( camera, name );					
			}
			break;

			default:				
				if( JumpThroughtChunk( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			break;
		}
	}

	 if( feof( inFile ) )
		return GEMERROR_LOAD;	

	return GEMERROR_OK;
}


DWORD gem_Scene::ReadEditorChunk( FILE* inFile )
{
	WORD				sign;
	LONG				size;
	LONG				pos;
	gem_Material*		mat;
	

	size = ReadDword( inFile ) - IDLENGTH;
	pos = ftell( inFile );

	while( ( !feof( inFile ) ) && ftell( inFile )<size+pos )
	{
		sign=ReadWord( inFile );
		switch (sign)
		{
			case GEMCHUNK_OBJECT:
				if ( ReadObjectChunk( inFile )!=GEMERROR_OK)
					return GEMERROR_LOAD;
			break;

			case GEMCHUNK_MATERIAL:
				if( dwFlags&GEMFLAGS_LOADMATERIALS )
				{
					mat = new gem_Material( this );
					if( mat->Load( inFile )!=GEMERROR_OK )
						return GEMERROR_LOAD;				

					MaterialList.AddItem( mat, mat->name );
				}
				else
					if( JumpThroughtChunk( inFile )!=GEMERROR_OK )
						return GEMERROR_LOAD;
			break;

			default:
				if( JumpThroughtChunk( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			break;
		}
	}

	if( feof( inFile) )
		return GEMERROR_LOAD;

	return GEMERROR_OK;
}


DWORD gem_Scene::Load( TCHAR* name )
{
	WORD			sign;
	FILE			*inFile;	

	if( !( inFile = fopen( name,"rb" ) ) )
		return GEMERROR_LOAD;

	sign = ReadWord( inFile );
	if (sign != GEMCHUNK_MAIN)
		return GEMERROR_LOAD;

	ReadDword( inFile );
	while (1)
	{
		sign = ReadWord( inFile );
		if( feof( inFile ) )
			break;

		switch( sign )
		{
			case GEMCHUNK_EDITOR:
				if( ReadEditorChunk( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			break;

			case GEMCHUNK_KEYFRAMER:					
				if( ReadKeyframerChunk( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;				
			break;

			case GEMCHUNK_VERSION:
				ReadDword( inFile );
				dwVersion = ReadDword( inFile );	
			break;

			default:
				if( JumpThroughtChunk( inFile )!=GEMERROR_OK )
					return GEMERROR_LOAD;
			break;
		}
	}

	fclose( inFile );

	LinkHierarchy();

	return GEMERROR_OK;
}

VOID gem_Scene::LinkHierarchy()
{
	gem_Keyframer*				keyframer;
	gem_Keyframer*				parent;

	for( DWORD i = 0; keyframer = KeyframerList.GetItem(i) ; i++ )
	{
		if( keyframer->parentID!=0xFFFF )
		{
			for( DWORD j = 0; parent = KeyframerList.GetItem(j) ; j++ )
				if( parent->hierarchyID==keyframer->parentID )
					keyframer->parent = parent;
		}
	}
}








			














