//
// Asura_Fragment_SystemMain.cpp
//
// Created by Richard Rice - 05/12/2002
// 
// Copyright (C) 2002 Rebellion, All Rights Reserved.
//

/*
Contains the Asura_Fragment_System functions that require the main asura engine (entities etc) to
compile/run. This is so Asura_Fragment_System can be used by max plugins
*/

#include "Core/Asura.h"
#include "Debugging/Asura_Assert.h"

#include "SpecialFX/Asura_Fragment_System.h"

#include "Entity/Ephemera/Asura_Entity_Fragment.h"
#include "Entity/Ephemera/Asura_Entity_AnimatedFragment.h"
#include "Shapes/Asura_Shape_System.h"
#include "Shapes/Asura_Shapes.h"
#include "Mesh/Asura_CollisionMesh.h"

#ifdef ASURA_TOOLS //for LoadFragments()
#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#endif

// D E B U G V A R S /////////////////////////////////////////////////////////
DEBUGVAR float dbg_fFragmentDefaultVelocity = 2.0f;
DEBUGVAR float dbg_fFragmentVelocityRandomness = 0.3f;

////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------**
** Asura_Fragment_System **
**-----------------------*/


#ifdef ASURA_TOOLS

void Asura_Fragment_System::LoadFragments()
{
#if ASURA_ENABLE_HASH_TO_FILENAME_LOOKUPS

	for(int i=0;i<Asura_Mesh_System::GetNumberOfMeshes();i++)
	{
		const Asura_Mesh* pxMesh = Asura_Mesh_System::GetMesh( i );

        //Does the mesh use fragments?
		Asura_Hash_ID uFragmentHash = pxMesh->m_uFragmentHash;
		if(uFragmentHash == ASURA_HASH_ID_UNSET) continue;

        if( !LoadSingleFragment(uFragmentHash) )
        {
            Asura_Assertf( false, ("Failed to load fragment data for mesh '%s'", pxMesh->Name.GetString()) );
        }
	}

#else

    Asura_Assert(0,"LoadFragments() needs ASURA_ENABLE_HASH_TO_FILENAME_LOOKUPS defined to work");

#endif
}


bool Asura_Fragment_System::LoadSingleFragment( Asura_Hash_ID uFragmentHash )
{
#if ASURA_ENABLE_HASH_TO_FILENAME_LOOKUPS

    if( GetFragmentType(uFragmentHash) )
    {
        // Already loaded so nothing to do here.
        return true;
    }

	const char* pxFilename = Asura_HashedFilename_System::GetFullFilename( uFragmentHash );
    if( !pxFilename )
    {
	    Asura_Assertf( false, ("Failed to determine filename for fragment 0x%08X", uFragmentHash) );
        return false;
    }

	if( !LoadAsuraFile(pxFilename) )
	{
		Asura_Assertf( false, ("Failed to load fragment file %s", pxFilename) );
		return false;
	}

    if( !GetFragmentType(uFragmentHash) )
    {
	    Asura_Assertf( false, ("Failed to find fragment type in %s", pxFilename) );
        return false;
    }

    return true;

#else

    Asura_Assert( false, "LoadSingleFragment() needs ASURA_ENABLE_HASH_TO_FILENAME_LOOKUPS defined to work" );
    return false;

#endif
}

#endif

Asura_Hash_ID Asura_Fragment_System::GetDestroyedShapeHash(Asura_Hash_ID uFragmentHash)
{
	Asura_FragmentType* pxFragmentType = GetFragmentType(uFragmentHash);
	if(!pxFragmentType) return ASURA_HASH_ID_UNSET;

	return pxFragmentType->GetDestroyedShapeHash();
}

void Asura_Fragment_System::GenerateFragments(Asura_Hash_ID uFragmentHash,const Asura_Vector_3& xPosition,const Asura_Matrix_3x3& xOrient,const Asura_Bounding_Box* pxBoundingBox,const Asura_Vector_3* pxVelocity/* = NULL*/,float fEnergy/*=0.0f*/)
{
	Asura_FragmentType* pxFragmentType = GetFragmentType(uFragmentHash);
	if(!pxFragmentType) return;

	pxFragmentType->GenerateFragments(xPosition,xOrient,pxBoundingBox,pxVelocity,fEnergy);
}

void Asura_Fragment_System::GenerateFragments(const Asura_Shape_Instance* pxShapeInstance,const Asura_Vector_3* pxVelocity /* = NULL */,float fEnergy/*=0.0f*/)
{
	Asura_Assert(pxShapeInstance,"");

	const Asura_Vector_3& xPosition = pxShapeInstance->GetPosition();
	const Asura_Matrix_3x3& xOrient = pxShapeInstance->GetOrientation();

	//find the shape used
	if(pxShapeInstance->GetShapeHashID() == ASURA_HASH_ID_UNSET) return;
	const Asura_Shape* pxShape = Asura_Shape_System::GetShape(pxShapeInstance->GetShapeHashID());
	if(!pxShape) return;

	//and get the fragment type and bounding box
	Asura_Hash_ID uFragmentHash = pxShape->GetFragmentHashID();
	const Asura_Bounding_Box* pxBoundingBox = 0;
	if(pxShape->GetLOSCollisionData()) 
	{
		pxBoundingBox = &pxShape->GetLOSCollisionData()->GetBoundingBox();
	}


	GenerateFragments(uFragmentHash,xPosition,xOrient,pxBoundingBox,pxVelocity,fEnergy);
}

////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------**
** Asura_FragmentType **
**--------------------*/



Asura_Hash_ID Asura_FragmentType::GetDestroyedShapeHash()
{
	for(int i=0;i<m_iNumberOfFragments;i++)
	{
		if(m_pxFragments[i].m_uFlags & ASURA_FRAGMENTFLAG_DESTROYEDSHAPE)
		{
			return m_pxFragments[i].m_uShapeHash;
		}
	}
	return ASURA_HASH_ID_UNSET;
}

void Asura_FragmentType::GenerateFragments(const Asura_Vector_3& xPosition,const Asura_Matrix_3x3& xOrient,const Asura_Bounding_Box* pxBoundingBox,const Asura_Vector_3* pxVelocity /* = NULL */,float fEnergy/*=0.0f*/)
{
	if(m_bAnimatedFragment)
	{
		//for animated fragment types create an animated fragment entity using skin and anim ID
		//taken from the fragments hash ID
		Asura_Entity_AnimatedFragment::Project_Create(m_uFragmentHash, m_uFragmentHash, xPosition, xOrient);
		return;
	}

	for(int i=0;i<m_iNumberOfFragments;i++)
	{
		Asura_Fragment* pxFragment = &m_pxFragments[i];
		if(pxFragment->m_uFlags & ASURA_FRAGMENTFLAG_DESTROYEDSHAPE) continue;

		for(int j=0;j<pxFragment->m_iQuantity;j++)
		{
			//get the relative fragment position
			Asura_Vector_3 xFragPosition;
			Asura_Vector_3 xFragVelocity;
			if(pxFragment->m_uFlags & ASURA_FRAGMENTFLAG_USEPOSITION)
			{
				xFragPosition = pxFragment->m_xPosition;
			}
			else 
			{
				if(pxBoundingBox)
				{
					xFragPosition.x = Asura_Random::GetFloat(pxBoundingBox->MinX,pxBoundingBox->MaxX);
					xFragPosition.y = Asura_Random::GetFloat(pxBoundingBox->MinY,pxBoundingBox->MaxY);
					xFragPosition.z = Asura_Random::GetFloat(pxBoundingBox->MinZ,pxBoundingBox->MaxZ);
				}
				else
				{
					xFragPosition.x = Asura_Random::GetFloat(-0.2f,0.2f);
					xFragPosition.y = Asura_Random::GetFloat(-0.2f,0.2f);
					xFragPosition.z = Asura_Random::GetFloat(-0.2f,0.2f);
				}
			}
			if(pxVelocity)
			{
				xFragVelocity = *pxVelocity;
				
				Asura_Vector_3 xRandomVector;
				xRandomVector.RandomDirection();
				xFragVelocity += (xRandomVector * fEnergy);
			}
			else
			{
				//get a velocity vector in the same direction
				xFragVelocity = xFragPosition;
				if(xFragVelocity.x==0 && xFragVelocity.y==0 && xFragVelocity.z==0)
				{
				    xFragVelocity.y = -1.0f;	
				}
				else
				{
					xFragVelocity.Normalise();
				}
			

				//randomise a bit
				xFragVelocity.x += Asura_Random::GetFloat(-dbg_fFragmentVelocityRandomness,dbg_fFragmentVelocityRandomness);
				xFragVelocity.y += Asura_Random::GetFloat(-dbg_fFragmentVelocityRandomness,dbg_fFragmentVelocityRandomness);
				xFragVelocity.z += Asura_Random::GetFloat(-dbg_fFragmentVelocityRandomness,dbg_fFragmentVelocityRandomness);

				xFragVelocity *= dbg_fFragmentDefaultVelocity * fEnergy;

				xFragVelocity = xFragVelocity * xOrient;
			}

			xFragPosition = xFragPosition * xOrient + xPosition;

			Asura_Entity_Fragment::Project_Create(pxFragment->m_uShapeHash,xFragPosition,xOrient,xFragVelocity);
		}
	}
}


