/* ==========================================================================
* 작 성 자 : 이순규
* 작 성 일 : 2006.12.15
* 내    용 : 장면 관리자
* 주의사항 : 
*===========================================================================*/
#pragma once

#include "SceneTree.h"
#include "CullingProcess.h"
#include "VisibleArray.h"
#include "VisibleArraySorter.h"
#include "IndexGenerator.h"

class cRay;
class cCamera;
class cTerrain;
class cAreaGroup;
class cAreaSceneNode;
class cAreaSceneNodeParam;
class cLightSceneNode;
class cLightSceneNodeParam;
class cSoundSceneNode;
class cSoundSceneNodeParam;
class cStaticSceneNode;
class cManagedStaticSceneNode;
class cStaticSceneNodeParam;
class cDynamicSceneNode;
class cHeroSceneNode;
class cPlayerSceneNode;
class cPlayerSceneNodeParam;
class cMonsterSceneNode;
class cTransMonSceneNode;
class cMonsterSceneNodeParam;
class cRefereeSceneNode;
class cNpcSceneNode;
class cNpcSceneNodeParam;
class cVehicleSceneNode;
class cVehicleSceneNodeParam;
class cEffectSceneNode;
class cEffectSceneNodeParam;
class cDropItemSceneNode;
class cDropItemSceneNodeParam;
class cMapPortalSceneNode;
class cMapPortalSceneNodeParam;
class cCardSceneNode;
class cCardSceneNodeParam;
class cGatheringSceneNode;
class cGatheringSceneNodeParam;

class cDepthPlane;
class cPlane;
class cShadowGeometry;

enum eNameCardOption;

/// 장면 파일 헤더
#pragma pack( push, 1 )
class cSceneFileHeader
{
public:
	/// 식별 코드
	char mCode[10];

	/// 버전
	unsigned int mVersion;

	/// 장면 트리 정보
	NiPoint3 mCenter;
	float mMinRadius;
	float mMaxRadius;

	/// 장면 노드 수
	unsigned int mNumSceneNodes;
};

class cSoundSceneFileHeader
{
public:
	/// 식별 코드
	char mCode[10];

	/// 버전
	unsigned int mVersion;

	/// 장면 노드 수
	unsigned int mNumSceneNodes;
};
#pragma pack( pop )

/// 장면 관리자
/// 장면 노드를 생성, 제거하는 관리자이다.
/// 장면을 렌더링하고,
/// 충돌 검사, 가시성 검사, 정렬 등의 작업을 수행한다.
class cSceneManager
{
	friend class cLightAgent;
	static cSceneManager* mSingleton;

public:
	cSceneManager();
	~cSceneManager();

	/// 어플리케이션 시작 및 종료 시점
	bool Init();
	void Exit();

	/// 지움
	void Clear();

	/// 초기화
	bool Init( const NiPoint3& center, float minRadius, float maxRadius );

	/// 로딩
//	bool Load( const cString& pathName );
	bool LoadEnv6( cFileLoader& loader );
	bool LoadEnv7( cFileLoader& loader );
	bool LoadEnv8( cFileLoader& loader );
	bool LoadEnv9( cFileLoader& loader );
	bool LoadEnv10( cFileLoader& loader );
	bool LoadGlobalArea6( cFileLoader& loader );
	bool LoadGlobalArea7( cFileLoader& loader );
	bool LoadGlobalArea8( cFileLoader& loader );
	bool LoadGlobalArea9( cFileLoader& loader );
	bool LoadGlobalArea10( cFileLoader& loader );
	bool LoadAreaGroup( unsigned int mapIndex );

	/// load process용 함수
	bool LoadSceneHeader( cFileLoader& loader, unsigned int& outNum );
	bool LoadSoundSceneHeader( cFileLoader& loader, unsigned int& outNum );

	/// return error event( -1:오류, 0:로딩완료, 1~:진행수)
	int LoadSceneNode( cFileLoader& loader, unsigned int loadCount=1 );
	int LoadSoundSceneNode( cFileLoader& loader, unsigned int loadCount=1 );

	int LoadSceneNode6( cFileLoader& loader, unsigned int loadCount );
	int LoadSceneNode7( cFileLoader& loader, unsigned int loadCount );
	int LoadSceneNode8( cFileLoader& loader, unsigned int loadCount );
	int LoadSceneNode9( cFileLoader& loader, unsigned int loadCount );
	int LoadSceneNode10( cFileLoader& loader, unsigned int loadCount );
	int LoadSceneNode11( cFileLoader& loader, unsigned int loadCount );

	int LoadSoundNode11( cFileLoader& loader, unsigned int loadCount );

	/// 처리
	void Process( unsigned long deltaTime, unsigned long accumTime );

	/// 렌더링
	void Render();
	void RenderZFalseDamage();
	void RenderShadow();

	/// 2D 객체 랜더링 (말풍선 etc.)
	void Render2D();

	/// 가시 레벨 거리
	void SetLevelDistance( float dist0, float dist1, float dist2 );

	/// 화면 상의 좌표에 대응하는 장면 노드들을 리턴
	/// 삼각형 단위로 검사한다.
	bool PickStatics( tArray<void*>* pickedArray, int mouseX, int mouseY, bool sortByDistance = false );
	bool PickDynamics( tArray<void*>* pickedArray, int mouseX, int mouseY, bool sortByDistance = false );
	bool PickManagedStatics( tArray<void*>* pickedArray, int mouseX, int mouseY, bool sortByDistance = false );

	/// 반직선과 충돌하는 장면 노드들을 수집
	/// 삼각형 단위로 검사한다.
	bool PickStatics( tArray<void*>* pickedArray, const cRay& ray, float maxDistance, bool sortByDistance = false );
	bool PickDynamics( tArray<void*>* pickedArray, const cRay& ray, float maxDistance, bool sortByDistance = false );
	bool PickManagedStatics( tArray<void*>* pickedArray, const cRay& ray, float maxDistance, bool sortByDistance = false );

	/// 구와 충돌하는 장면 노드들을 수집
	/// 구 단위로 검사한다.
	bool Pick_Kind( tArray<void*>* pickedArray, const cSphere& sphere, cSceneNode::eType type );
	bool Pick_Type( tArray<void*>* pickedArray, const cSphere& sphere, cSceneNode::eType type );
	bool PickAreas( tArray<void*>* pickedArray, const cSphere& sphere );
	bool PickLights( tArray<void*>* pickedArray, const cSphere& sphere );
	bool PickSounds( tArray<void*>* pickedArray, const cSphere& sphere );

	/// 영역 장면 노드를 생성
	cAreaSceneNode* CreateArea( const cAreaSceneNodeParam& param );

	/// 조명 장면 노드를 생성
	cLightSceneNode* CreateLight( const cLightSceneNodeParam& param );

	/// 사운드 장면 노드를 생성
	cSoundSceneNode* CreateMapSound( const cSoundSceneNodeParam& param );
	cSoundSceneNode* CreateEffectSound( const cSoundSceneNodeParam& param );

	void UpdateSoundGain();

	void ActiveSoundNodeAll( bool active );

	/// 정적 장면 노드를 생성
	cStaticSceneNode* CreateStatic( const cStaticSceneNodeParam& param );
	cDropItemSceneNode* CreateDropItem( const cDropItemSceneNodeParam& param );
	cGatheringSceneNode* CreateGathering( const cGatheringSceneNodeParam& param );
	cMapPortalSceneNode* CreateMapPortal(  unsigned long scriptIdx, NiPoint3 pos, NiMatrix3 rot );
	cCardSceneNode* CreateCard( const cCardSceneNodeParam& param );

	/// 관리 객체 생성용 외부 인터페이스
	cPlayerSceneNode* CreatePlayer( const cPlayerSceneNodeParam& param );
	cMonsterSceneNode* CreateMonster( const cMonsterSceneNodeParam& param );
	cTransMonSceneNode* CreateTransMonster( const cMonsterSceneNodeParam& param, bool IsHero );
	cNpcSceneNode* CreateNPC( const cNpcSceneNodeParam& param );
	cEffectSceneNode* CreateEffect( const cEffectSceneNodeParam& param );
	cEffectSceneNode* CreateSelfEffect( cString pathName, bool distCheck, NiTransform& trans, bool bLoop );
	cEffectSceneNode* CreateDamageEffect( const cEffectSceneNodeParam& param, bool IsMiss, bool zFalse );
	cHeroSceneNode* CreateHero( const cPlayerSceneNodeParam& param );
	cVehicleSceneNode* CreateVehicle( const cVehicleSceneNodeParam& param );

	/// SceneManager내부 연결을 임의로 제외/추가 한다.( ex-탈것에 링크건 PlayerSceneNode )
	bool AddPlayer( cPlayerSceneNode* node );
	bool DelPlayer( cPlayerSceneNode* node );

	cRefereeSceneNode* CreateReferee( const cSceneNodeParam& param );

	/// 관리 객체의 제거용 외부 인터페이스
	bool DestroyDynamic( cSceneNode* node );
	bool DestroyDropItemNode( cSceneNode* node );
	bool DestroyMapPortalNode( cSceneNode* node );
	bool DestroyGathringNode( cSceneNode* node );
	bool DestroyStaticNode( cSceneNode* node );

	/// Deletelist에서 제거하지 않고 외부에서 임의로 호출하는 경우는 DelListRemove = ture로 설정하시오
	bool DestroyEffectNode( cSceneNode* node );
	bool DestroySoundNode( cSceneNode* node );

	/// 제거 되어야 할 이펙트 노드에 대한 처리
	void AddDeleteEffectNode( cEffectSceneNode* node );

	void AddDeleteGatheringNode( cGatheringSceneNode* node );

	/// 제거 되어야 할 사운드 노드에 대한 처리
	void AddDeleteSoundNode( cSoundSceneNode* node );

	/// 모든 노드들을 제거
	void DestroyAll();

	/// 전체 영역 노드
	cAreaSceneNode* GetGlobalArea();

	/// 그림자 렌더링 속성
	NiZBufferProperty* GetShadowZBuffProperty() const;
	NiAlphaProperty* GetShadowAlphaProperty() const;
	NiTexturingProperty* GetShadowTexProperty() const;

	void AddVisibleShadowGeom( cShadowGeometry* geom );

	void AddPlane( cPlane* pPlane );

	void AddVisibleDynamicNode( cDynamicSceneNode* node );
	///

	inline void SetScreenShotMode( bool enable ) { mScreenShotMode = enable; }
	inline bool IsScreenShotMode() { return mScreenShotMode; }

	inline tArray<void*>& GetVisibleDynamicSceneArray() { return mVisibleDynamicNodeArray; }

	unsigned int CalcVisibleLevel( cStaticSceneNode* n );
	bool CalcDynamicVisibleLevel( cSceneNode* n );


	cEffectSceneNode* GetEffectSceneNode( unsigned long effectIdx );
	cSoundSceneNode* GetSoundSceneNode( unsigned long soundIdx );
	cDynamicSceneNode* GetDynamicSceneNode( unsigned long dynIdx );
	cDropItemSceneNode* GetDropItemSceneNode( unsigned long nodeIdx );
	cGatheringSceneNode* GetGatheringSceneNode( unsigned long nodeIdx );
	cStaticSceneNode* GetStaticSceneNode( unsigned long nodeIdx );

	cManagedStaticSceneNode* GetManagedStaticNode( unsigned int type, unsigned long nodeIdx );

	/// 검증코드
	bool IsValidEffectNode( cEffectSceneNode* node );
	bool IsValidEffectNode( unsigned long idx, cEffectSceneNode* node );

	bool IsValidSoundNode( cSoundSceneNode* node );
	bool IsValidSoundNode( unsigned long idx, cSoundSceneNode* node );

	bool IsValidDynamicNode( cDynamicSceneNode* node );
	bool IsValidDynamicNode( unsigned long idx, cDynamicSceneNode* node );

protected:
	/// 노드를 추가
	bool AddAreaNode( cSceneNode* node );
	bool AddLightNode( cSceneNode* node );
	bool AddSoundNode( cSceneNode* node );
	bool AddStaticNode( cSceneNode* node );
	bool AddDropItemNode( cSceneNode* node );
	bool AddGatheringNode( cSceneNode* node );
	bool AddMapPotalNode( cSceneNode* node );
	bool AddDynamicNode( cSceneNode* node );
	bool AddEffectNode( cSceneNode* node );

public:
	/// 단일체를 리턴
	static cSceneManager* GetSingleton();

protected:
	/// 버전
	unsigned int mVersion;

	cIndexGenerator<unsigned long> mAreaNodeIndexGen;
	cIndexGenerator<unsigned long> mLightNodeIndexGen;
	cIndexGenerator<unsigned long> mSoundIndexGen;
	cIndexGenerator<unsigned long> mStaticNodeIndexGen;
	cIndexGenerator<unsigned long> mDropItemNodeIndexGen;
	cIndexGenerator<unsigned long> mGatheringNodeIndexGen;
	cIndexGenerator<unsigned long> mMapPortalNodeIndexGen;
	cIndexGenerator<unsigned long> mDynamicNodeIndexGen;
	cIndexGenerator<unsigned long> mEffectIndexGen;

	typedef tPointerHashMap<unsigned long, void*> cAreaMap;
	typedef tPointerHashMap<unsigned long, void*> cLightMap;
	typedef tPointerHashMap<unsigned long, void*> cSoundMap;
	typedef tPointerHashMap<unsigned long, void*> cNodeMap;


	/// 관리 객체들은 순서 상관 없으므로 배열 처리 한다.
	/// 추가 삭제가 빈번하지만, 순서와 상관 없으므로, 마지막 객체로 빈곳을 채운다.
//	typedef tArray<void*> cAreaArray;
//	typedef tArray<void*> cLightArray;
//	typedef tArray<void*> cSoundArray;
//	typedef tArray<void*> cNodeArray;

	/// 트리
	/// 공간을 분할하여 노드들을 계층적으로 관리한다.
	/// 충돌 검사 및 컬링을 효율적으로 수행하기위해 사용한다.
	cSceneTree mNodeTree;

	cAreaMap mAreaArray;
	cLightMap mLightArray;
	cSoundMap mSoundArray;

	/// 캐릭터 (PC, NPC, Monster)
	cNodeMap mDynamicObjectArray;

	/// 아이템
	cNodeMap mDropItemArray;

	/// 채집 객체
	cNodeMap mGatheringArray;

	/// 맵이동 Potal객체
	cNodeMap	mMapPortalArray;

	/// 환경 객체 노드 배열
	cNodeMap mStaticObjectArray;

	/// 이펙트 (트리로 관리할 필요 없음)
	cNodeMap mEffectNodeArray;

	/// 이펙트 삭제용
//	typedef tPointerList<cSceneNode*> cDelEffectList;
	typedef tPointerList<void*> cDelEffectList;
	cDelEffectList mDelEffectList;

	/// 	
	typedef tPointerList<void*> cDelGatheringList;
	cDelGatheringList mDelGatheringList;

	/// 사운드 삭제용
	typedef tPointerList<void*> cDelSoundList;
	cDelSoundList mDelSoundList;

	/// 2D 평면 객체 관리
//	typedef tArray<cPlane*> cPlaneArray;
	typedef tArray<void*> cPlaneArray;
	cPlaneArray	mPlaneArray;

	/// 게임 BaseObj에 대응한 SceneNode를 거리별 Sorting하기위해 사용한다.
	tArray<void*> mVisibleDynamicNodeArray;

	/// 가시 레벨 거리
	float mSqaredLevelDistance0;
	float mSqaredLevelDistance1;
	float mSqaredLevelDistance2;

	/// 컬링
	cCullingProcess mCuller;
	cCullingProcess mDamageCuller;

	/// 반투명 노드 가시 배열
	cVisibleArray mAlphaArray;
	cVisibleArray mAlphaTestArray;
	cVisibleArray mDamageArray;

	/// 반투명 노드 가시 배열 정렬자
	cAlphaArraySorter mAlphaArraySorter;
	cAlphaArraySorter mDamageSorter;

	/// 불투명 노드 가시 배열
	cVisibleArray mSolidArray;

	/// 불투명 노드 가시 배열 정렬자
	cSolidArraySorter mSolidArraySorter;

	/// 픽킹된 장면 노드 배열
	typedef tArray<void*> cPickedArray;
	cPickedArray mPickedArray;

	/// 전체 영역 노드
	cAreaSceneNode* mGlobalArea;

	/// 영역 그룹
	typedef tHashMap<unsigned int, void*> cAreaGroupMap;
	cAreaGroupMap mAreaGroupMap;

	/// 텍스처 맵
	typedef tHashMap<unsigned int, void*> cTextureMap;
	cTextureMap mTextureMap;

	/// 그림자 렌더링 속성
	NiZBufferPropertyPtr mShadowZBuffProp;
	NiAlphaPropertyPtr mShadowAlphaProp;
	NiTexturingPropertyPtr mShadowTexProp;

	/// 그림자 가시 배열
	typedef tArray<cShadowGeometry*> cShadowGeomArray;
	cShadowGeomArray mShadowGeomArray;

	///
	bool mScreenShotMode;

	NiPoint3 mHeroPos;
};

inline
cAreaSceneNode* cSceneManager::GetGlobalArea()
{
	return mGlobalArea;
}

inline
void cSceneManager::SetLevelDistance( float dist0, float dist1, float dist2 )
{
	mSqaredLevelDistance0 = dist0 * dist0;
	mSqaredLevelDistance1 = dist1 * dist1;
	mSqaredLevelDistance2 = dist2 * dist2;
}

inline
NiZBufferProperty* cSceneManager::GetShadowZBuffProperty() const
{
	return mShadowZBuffProp;
}

inline
NiAlphaProperty* cSceneManager::GetShadowAlphaProperty() const
{
	return mShadowAlphaProp;
}

inline
NiTexturingProperty* cSceneManager::GetShadowTexProperty() const
{
	return mShadowTexProp;
}

inline
void cSceneManager::AddVisibleShadowGeom( cShadowGeometry* geom )
{
	assert( geom );

	mShadowGeomArray.PushBack( geom );
}

inline 
cEffectSceneNode* cSceneManager::GetEffectSceneNode( unsigned long effectIdx ) 
{ 
	return (cEffectSceneNode*)mEffectNodeArray.GetAt( effectIdx ); 
}

inline 
cSoundSceneNode* cSceneManager::GetSoundSceneNode( unsigned long soundIdx ) 
{ 
	return (cSoundSceneNode*)mSoundArray.GetAt( soundIdx ); 
}

inline 
cDynamicSceneNode* cSceneManager::GetDynamicSceneNode( unsigned long dynIdx ) 
{ 
	return (cDynamicSceneNode*)mDynamicObjectArray.GetAt( dynIdx ); 
}

inline 
cDropItemSceneNode* cSceneManager::GetDropItemSceneNode( unsigned long nodeIdx ) 
{ 
	return (cDropItemSceneNode*)mDropItemArray.GetAt( nodeIdx ); 
}

inline 
cGatheringSceneNode* cSceneManager::GetGatheringSceneNode( unsigned long nodeIdx ) 
{ 
	return (cGatheringSceneNode*)mGatheringArray.GetAt( nodeIdx ); 
}

inline 
cStaticSceneNode* cSceneManager::GetStaticSceneNode( unsigned long nodeIdx ) 
{ 
	return (cStaticSceneNode*) mStaticObjectArray.GetAt( nodeIdx ); 
}


inline
cSceneManager* cSceneManager::GetSingleton()
{
	return mSingleton;
}

#define SCENEMAN cSceneManager::GetSingleton()

/*// 장면 컬러
/// 컬링 프로세스에 대한 커스터 마이징 이전까지 사용
class cSceneCuller : public NiCullingProcess
{
public:
	cSceneCuller( const cCamera* cam, const NiPoint3& heroPos, float squaredDist0, float squaredDist1, float squaredDist2 );

	/// 가시 레벨
	unsigned int GetVisibleLevel( const NiPoint3& pos );

public:
	const cCamera* mCamera;
	NiPoint3 mHeroPos;
	float mSqaredLevelDistance0;
	float mSqaredLevelDistance1;
	float mSqaredLevelDistance2;
};
//*/
