#include "stdafx.h"
#include "Terrain.h"

#include "RenderSystem.h"
#include "ResourceManager.h"
#include "FogAgent.h"
#include "TerrainNode.h"

static const char* gTerrainFileCode = "IrisTerrain";

bool cTerrain::LoadTexture( unsigned int index, const cString& pathName )
{
	assert( index < TERRAIN_TEXTURE_COUNT );

	/// 텍스처 로딩
	cFileLoader loader;
	if( loader.Open( pathName, true ) == false )
	{
		assert( 0 );
		return false;
	}

	cRenderer* renderer = RENDERSYS->GetRenderer();
	LPDIRECT3DTEXTURE9 tex = 0;
	HRESULT ret = ::D3DXCreateTextureFromFileInMemory( renderer->GetD3DDevice(), loader.GetBufferPtr(), loader.GetSize(), &tex );

	if( FAILED(ret) || tex == 0 )
	{
		assert( 0 );
		return false;
	}

	///
	delete mTextures[index];
	mTextures[index] = 0;

	new cTerrainTexture( index, tex );
	return true;
}

bool cTerrain::LoadHeader( unsigned int mapIndex, const cString& pathName )
{
	Clear();

	/// 기본 텍스처를 로딩
	LoadTexture( 0, "./Data/2DData/detail0.tga" );

	/// 파일 열기
	assert( mLoader.GetSize() == 0 );
	cFileLoader& loader = mLoader;

	if( loader.Open( pathName, true ) == false )
	{
		assert( 0 && "failed to open file to load terrain" );
		return false;
	}

	/// 파일 헤더 로딩
	cTerrainFileHeader header;

	if( loader.Read( &header, sizeof(cTerrainFileHeader) ) != sizeof(cTerrainFileHeader) )
	{
		assert( 0 && "failed to load terrain file header" );
		return false;
	}

	/// 헤더 검사
	if( memcmp( header.mCode, gTerrainFileCode, 12 ) != 0 )
	{
		assert( 0 && "invalid file type" );
		return false;
	}
	switch( header.mVersion )
	{
	case 2:
	case 3:
	case 4:
		--header.mCellCount;
		break;
	case 5:
	case 6:
		break;
	default:
		assert( 0 && "invalid file version" );
		return false;
	}

	/// 버전을 설정
	cTerrainLeafNode::mVersion = header.mVersion;

	/// 그리드 크기를 검사
	if( CheckCellCount( header.mCellCount ) == false )
	{
		assert( 0 && "invalid grid size" );
		return false;
	}

	/// 단위 설정
	mCellCount = header.mCellCount;
	mLineCount = mCellCount + 1;
	mMetersPerVertex = header.mMetersPerVertex;
	mUnitsPerMeter = header.mUnitsPerMeter;
	mUnitsPerVertex = mMetersPerVertex * (float)mUnitsPerMeter;
	mUnitsPerLeafNode = TERRAIN_LEAF_CELL_COUNT * mUnitsPerVertex;
	mSegmentLength = float(mCellCount) * mUnitsPerVertex;

	/// 리프 노드 배열의 용량을 설정
	unsigned int leafCount = mCellCount / TERRAIN_LEAF_CELL_COUNT;
	unsigned int numNodes = leafCount * leafCount;
	mNodeArray.Resize( numNodes );

	/// 가시 노드 배열의 용량을 설정
	mVisibleArray.Reserve( numNodes );

	/// 텍스처 배열을 로딩
	cString texPathName;
	unsigned int iend = 0;
	switch( header.mVersion )
	{
	case 2:
		iend = TERRAIN_TEXTURE_COUNT_VER2;
		break;
	case 3:
		iend = TERRAIN_TEXTURE_COUNT;
		break;
	case 4:
	case 5:
	case 6:
		break;
	}

	switch( header.mVersion )
	{
	case 2:
	case 3:
		{
			char texName[64];

			for( unsigned int i = 1; i < iend; ++i )
			{
				if( loader.Read( texName, 64 ) != 64 )
				{
					assert( 0 && "failed to load terrain texture name" );
					return false;
				}
				if( texName[0] == 0 && texName[1] == 0 )
					continue;

				texName[63] = 0;
				texPathName.Format( "./Map/Map%02d/", mapIndex );
				texPathName += texName;

				if( LoadTexture( i, texPathName ) == false )
				{
					assert( 0 && "failed to load terrain texture" );
				}
			}
			break;
		}
	case 4:
	case 5:
	case 6:
		{
			unsigned int texIndex = 0;
			char texName[64];

			for( unsigned int i = 0, iend = header.mNumTextures; i < iend; ++i )
			{
				texIndex = 0;
				loader.ReadUnsignedInt( &texIndex );
				loader.Read( texName, 64 );

				if( texIndex == 0 )
					continue;
				if( texName[0] == 0 && texName[1] == 0 )
					continue;

				texName[63] = 0;
				texPathName.Format( "./Map/Map%02d/", mapIndex );
				texPathName += texName;

				if( LoadTexture( texIndex, texPathName ) == false )
				{
					assert( 0 && "failed to load terrain texture" );
				}
			}
			break;
		}
	}

	/// 높이맵, 알파맵, 색상맵을 로딩
	unsigned int numVerts = mLineCount * mLineCount;
	mHeights = new float[numVerts];
	mNormals = NiNew NiPoint3[numVerts];
	mColors = NiNew NiColor[numVerts];
	mAlphas = NiNew NiPoint3[numVerts];

	switch( header.mVersion )
	{
	case 2:
	case 3:
	case 4:
		break;
	case 5:
	case 6:
		loader.Read( mHeights, sizeof(float) * numVerts );
		loader.Read( mAlphas, sizeof(NiPoint3) * numVerts );
		loader.Read( mColors, sizeof(NiColor) * numVerts );
		ComputeNormals( 0, 0, mLineCount, mLineCount );
		break;
	}

	/// 버퍼 생성 및 초기화
	unsigned int buffCount = mCellCount / TERRAIN_BUFF_CELL_COUNT;
	mBuffer = new cTerrainBuffer*[buffCount];

	for( unsigned int yi = 0; yi < buffCount; ++yi )
	{
		mBuffer[yi] = new cTerrainBuffer[buffCount];
	}

	switch( header.mVersion )
	{
	case 2:
	case 3:
	case 4:
		for( unsigned int yi = 0; yi < buffCount; ++yi )
		{
			for( unsigned int xi = 0; xi < buffCount; ++xi )
			{
				mBuffer[yi][xi].Init( 0, xi * TERRAIN_BUFF_CELL_COUNT, yi * TERRAIN_BUFF_CELL_COUNT );
			}
		}
		break;
	case 5:
	case 6:
		for( unsigned int yi = 0; yi < buffCount; ++yi )
		{
			for( unsigned int xi = 0; xi < buffCount; ++xi )
			{
				mBuffer[yi][xi].Init( this, xi * TERRAIN_BUFF_CELL_COUNT, yi * TERRAIN_BUFF_CELL_COUNT );
			}
		}
		break;
	}

	/// 셰이더를 초기화
	cRenderer* renderer = RENDERSYS->GetRenderer();
	mShader.Init( renderer->GetTerrainEffect() );

	/// 정점 선언을 생성
	LPDIRECT3DDEVICE9 device = renderer->GetD3DDevice();

	D3DVERTEXELEMENT9 decl[] = 
	{
		{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
		{ 1, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   0 },
		{ 2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0 },
		{ 3, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
		D3DDECL_END()
	};

	device->CreateVertexDeclaration( decl, &mVertexDeclaration );

//*
	/// 인덱스 버퍼를 생성
	unsigned int indexCount = 6 * TERRAIN_LEAF_CELL_COUNT * TERRAIN_LEAF_CELL_COUNT;
	unsigned short indexArray[6 * TERRAIN_LEAF_CELL_COUNT * TERRAIN_LEAF_CELL_COUNT];
	unsigned short* p = indexArray;

	for( unsigned int yi=0, yend=TERRAIN_LEAF_CELL_COUNT; yi<yend; ++yi )
	{
		for( unsigned int xi = 0, xend = TERRAIN_LEAF_CELL_COUNT; xi < xend; ++xi )
		{
			*p = (unsigned short)(xi + yi*TERRAIN_BUFF_LINE_COUNT);++p;
			*p = (unsigned short)(xi + (yi+1)*TERRAIN_BUFF_LINE_COUNT);++p;
			*p = (unsigned short)((xi+1) + yi*TERRAIN_BUFF_LINE_COUNT);++p;

			*p = (unsigned short)(xi+1 + yi*TERRAIN_BUFF_LINE_COUNT);++p;
			*p = (unsigned short)(xi + (yi+1)*TERRAIN_BUFF_LINE_COUNT);++p;
			*p = (unsigned short)(xi+1 + (yi+1)*TERRAIN_BUFF_LINE_COUNT);++p;
		}
	}
//*/

/*
	unsigned int indexCount = TERRAIN_LEAF_LINE_COUNT_X2;
	unsigned short indexArray[TERRAIN_LEAF_LINE_COUNT_X2];
	unsigned short* p = indexArray;

	for( unsigned int xi = 0, xend = TERRAIN_LEAF_LINE_COUNT; xi < xend; ++xi )
	{
		*p = (unsigned short)(xi); ++p;
		*p = (unsigned short)(xi + TERRAIN_BUFF_LINE_COUNT); ++p;
	}
//*/

	mIndexBuffer = renderer->CreateIndexBuffer( indexCount, indexArray );
	assert( mIndexBuffer );

	/// 루트 노드 생성
	mRootNode = new cTerrainBranchNode( 0, 0, mCellCount );

	/// 로딩 준비
	assert( mLoadArray == 0 );
	mLoadArray = new cTerrainNode*[MAX_NODES];
	assert( mLoadArray );
	mLoadCount = 0;
	mLoadIndex = 0;

	mRootNode->AddToLoadArray( mLoadArray, mLoadCount );
	return true;
}

int cTerrain::LoadNodes( unsigned int count )
{
	assert( mLoadArray );

	unsigned int i = 0;

	for( ; i < count; ++i, ++mLoadIndex )
	{
		if( mLoadIndex >= mLoadCount )
		{
			delete [] mLoadArray;
			mLoadArray = 0;

			mLoader.Close();
			count = i;
			return 1;
		}

		if( mLoadArray[mLoadIndex]->Load( mLoader ) == false )
		{
			count = i;
			return -1;
		}
	}

	count = i;
	return 0;
}
