/**
 * \file Terrain.cpp
 * \brief CTerrain member class functions implementation. CTerrain manages all static meshes on the game level.
 *
 * \author Petar Bajic, MPE (C) All Rights Reserved, Homepage: www.mystic-peanut.com
 * \date January, 16 2007.
 */

#include "Terrain.h"
#include "Particles.h"

#pragma warning( disable : 4996 ) // disable deprecated warning
#include <strsafe.h>
#pragma warning( default : 4996 )

/**
 * \brief Standard constructor
 * \author Petar Bajic
 * \date January, 16 2007.
 */
CTerrain::CTerrain()
{
	m_Base = NULL;
	m_Sky = NULL;
	m_Clouds = NULL;
	m_Fence = NULL;
	m_Grass = NULL;
	m_Grass2 = NULL;
	m_Grass3 = NULL;
	m_Hill1 = NULL;
	m_Hill2 = NULL;
	m_Hill3 = NULL;
	m_Forest = NULL;
	m_3DForest = NULL;
	m_Tractor = NULL;

	m_Barrel = NULL;
	m_Trough = NULL;
	m_Shed = NULL;
	m_Tree1 = NULL;
	m_Tree2 = NULL;
	m_Tree3 = NULL;
	m_House = NULL;
	m_Barn = NULL;

	m_Bird = NULL;
}

/**
 * \brief Standard destructor.
 * \author Petar Bajic
 * \date January, 16 2007.
 */
CTerrain::~CTerrain()
{
	SAFE_DELETE(m_Base);
	SAFE_DELETE(m_Sky);
	SAFE_DELETE(m_Clouds);
	SAFE_DELETE(m_Fence);
	SAFE_DELETE(m_Grass);
	SAFE_DELETE(m_Grass2);
	SAFE_DELETE(m_Grass3);
	SAFE_DELETE(m_Hill1);
	SAFE_DELETE(m_Hill2);
	SAFE_DELETE(m_Hill3);
	SAFE_DELETE(m_Forest);
	SAFE_DELETE(m_3DForest);
	SAFE_DELETE(m_Tractor);

	SAFE_DELETE(m_Barrel);
	SAFE_DELETE(m_Trough);
	SAFE_DELETE(m_Shed);
	SAFE_DELETE(m_Tree1);
	SAFE_DELETE(m_Tree2);
	SAFE_DELETE(m_Tree3);
	SAFE_DELETE(m_House);
	SAFE_DELETE(m_Barn);

	SAFE_DELETE(m_Bird);
}

/**
 * \brief Init function loads terrain models.
 * \author Petar Bajic
 * \date January, 16 2007.
 */
bool CTerrain::Init(HWND hWnd, LPDIRECT3DDEVICE9 pd3dDevice, int screenWidth, int screenHeight)
{
	//Load Static Models for 3D menu environment
	WCHAR strSearchDir[MAX_PATH]= L"Media//Models//";
	WCHAR strFullPath[MAX_PATH];
	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"terrain.x");
	m_Base = new CModelStatic();
	if (!(m_Base->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/terrain.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"sky.x");
	m_Sky = new CModelStatic();
	if (!(m_Sky->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/sky.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"clouds.x");
	m_Clouds = new CModelStatic();
	if (!(m_Clouds->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/clouds.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"fence.x");
	m_Fence = new CModelStatic();
	if (!(m_Fence->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/fence.x file",L"Error",MB_OK);
		return false;
	}

    StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"tractor.x");
	m_Tractor = new CModelStatic();
	if (!(m_Tractor->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/tractor.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"hill1.x");
	m_Hill1 = new CModelStatic();
	if (!(m_Hill1->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/hill1.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"hill2.x");
	m_Hill2 = new CModelStatic();
	if (!(m_Hill2->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/hill2.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"hill3.x");
	m_Hill3 = new CModelStatic();
	if (!(m_Hill3->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/hill3.x file",L"Error",MB_OK);
		return false;
	}


	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"barrel.x");
	m_Barrel = new CModelStatic();
	if (!(m_Barrel->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/barrel.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"trough.x");
	m_Trough = new CModelStatic();
	if (!(m_Trough->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/trough.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"shed.x");
	m_Shed = new CModelStatic();
	if (!(m_Shed->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/shed.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"tree1.x");
	m_Tree1 = new CModelStatic();
	if (!(m_Tree1->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/tree1.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"tree2.x");
	m_Tree2 = new CModelStatic();
	if (!(m_Tree2->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/tree2.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"tree3.x");
	m_Tree3 = new CModelStatic();
	if (!(m_Tree3->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/tree3.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"barn.x");
	m_Barn = new CModelStatic();
	if (!(m_Barn->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/barn.x file",L"Error",MB_OK);
		return false;
	}

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"house.x");
	m_House = new CModelStatic();
	if (!(m_House->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/house.x file",L"Error",MB_OK);
		return false;
	}

	//Load Forest
	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"forest.x");
	m_Forest = new CForest();
	if (!(m_Forest->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/forest.x file",L"Error",MB_OK);
		return false;
	}
	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"forestree.x");
	m_3DForest = new C3DForest();
	if (!(m_3DForest->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/forestree.x file",L"Error",MB_OK);
		return false;
	}

	//Init Grass
	m_Grass = new CPGrass(2048, 1.5f, 0.0f, 40.0f, 0); //2048 particles for grass
	m_Grass->init(pd3dDevice,L"Media/Pics/grass.png");

	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"grass_floor.x");
	m_Grass2 = new CModelStatic();
	if (!(m_Grass2->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/grass_floor.x file",L"Error",MB_OK);
		return false;
	}
	StringCchCopy(strFullPath,MAX_PATH,strSearchDir);
	StringCchCat( strFullPath, MAX_PATH, L"tall_grass.x");
	m_Grass3 = new CModelStatic();
	if (!(m_Grass3->Load(pd3dDevice, strSearchDir, strFullPath)))
	{
		MessageBox(hWnd,L"Could not load Media/Models/tall_grass.x file",L"Error",MB_OK);
		return false;
	}

	m_Bird = new CXFileEntity(); // Create our entity class
	// Load bird x file
	GetCurrentDirectory(MAX_PATH,strFullPath);
	StringCchCat(strFullPath, 255, L"//Media//Models//bird.x");
	HRESULT hr = m_Bird->LoadXFile(pd3dDevice,strFullPath);
	if (FAILED(hr))
	{
		MessageBox(hWnd,L"Could not load Media/Models/bird.x file",L"Error",MB_OK);
		return false;
	}

	return true;
}

/**
 * \brief Its important to call this function BEFORE device is reset, like when switching to/from fullscreen mode.
 * \author Petar Bajic
 * \date January, 16 2007.
 */
void CTerrain::OnLostDevice()
{
	SAFE_DELETE(m_Grass);
}

/**
 * \brief Its important to call this function AFTER device is reset, like when switching to/from fullscreen mode.
 * \author Petar Bajic
 * \date January, 16 2007.
 */
void CTerrain::OnResetDevice(LPDIRECT3DDEVICE9 pd3dDevice)
{
	//Init Grass
	m_Grass = new CPGrass(2048, 1.5f, 0.0f, 40.0f, 0); //2048 particles for grass
	m_Grass->init(pd3dDevice,L"Media/Pics/grass.png");
}

/**
 * \brief Render function draws terrain models on the screen.
 * \param details boolean telling if details should be rendered (on slower graphics, low details can speed up framerate)
 * \author Petar Bajic
 * \date January, 16 2007.
 */
void CTerrain::OnRender(LPDIRECT3DDEVICE9 pd3dDevice, bool details)
{
	D3DXMATRIXA16 matWorld;

	//Livestock
	m_Bird->Render(pd3dDevice);

	//Big stuff
	m_Base->OnRender(pd3dDevice);
	m_Hill1->OnRender(pd3dDevice);
	m_Hill2->OnRender(pd3dDevice);
	m_Hill3->OnRender(pd3dDevice);
	pd3dDevice->LightEnable( 0, FALSE );
	pd3dDevice->LightEnable( 1, FALSE );
	pd3dDevice->LightEnable( 2, TRUE );
	m_Sky->OnRender(pd3dDevice);
	m_Clouds->OnRender(pd3dDevice);
	pd3dDevice->LightEnable( 2, FALSE );
	pd3dDevice->LightEnable( 0, TRUE );
	pd3dDevice->LightEnable( 1, TRUE );
	m_3DForest->OnRender(pd3dDevice);

	//Smaller Items
	m_Barrel->OnRender(pd3dDevice);
	m_Fence->OnRender(pd3dDevice);
	m_Trough->OnRender(pd3dDevice);
	m_Shed->OnRender(pd3dDevice);
	m_Tree1->OnRender(pd3dDevice);
	m_House->OnRender(pd3dDevice);
	m_Barn->OnRender(pd3dDevice);
	m_Tractor->OnRender(pd3dDevice);

	//Fences
	D3DXVECTOR3 pos = D3DXVECTOR3(33.0f, -1.6f, -61.0f);
	float rot = 1.0f;
	m_Fence->OnFrameMove(pd3dDevice, rot, pos);
	m_Fence->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(64.0f, -1.6f, -54.0f);
	m_Fence->OnFrameMove(pd3dDevice, rot, pos);
	m_Fence->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(64.0f, -1.6f, -34.0f);
	rot = -0.92f;
	m_Fence->OnFrameMove(pd3dDevice, rot, pos);
	m_Fence->OnRender(pd3dDevice);

	//center floor mesh with higher resolution grass texture
	m_Grass2->OnRender(pd3dDevice);

	//tall grass bilboards
	pos = D3DXVECTOR3(-12.0f, 1.9f, 64.0f);
	rot = 1.4f;
	m_Grass3->OnFrameMove(pd3dDevice, rot, pos);
	m_Grass3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(12.0f, 1.9f, 60.0f);
	rot = 1.8f;
	m_Grass3->OnFrameMove(pd3dDevice, rot, pos);
	m_Grass3->OnRender(pd3dDevice);

	pos = D3DXVECTOR3(-23.0f, 1.7f, 53.0f);
	rot = 1.4f;
	m_Grass3->OnFrameMove(pd3dDevice, rot, pos);
	m_Grass3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(1.0f, 1.7f, 51.5f);
	rot = 1.65f;
	m_Grass3->OnFrameMove(pd3dDevice, rot, pos);
	m_Grass3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(23.0f, 1.7f, 46.0f);
	rot = 2.1f;
	m_Grass3->OnFrameMove(pd3dDevice, rot, pos);
	m_Grass3->OnRender(pd3dDevice);


	D3DXMatrixIdentity(&matWorld);
	pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

	if(details)
	{
		m_Grass->Render();
	}

	///Treez 2
	m_Tree2->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-94.0f, 7.0f, 15.0f);
	rot = 1.0f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree2->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-94.0f, 9.0f, -15.0f);
	rot = 2.0f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree2->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-84.0f, 9.0f, -35.0f);
	rot = 0.4f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree2->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-104.0f, 9.0f, -25.0f);
	rot = 0.0f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree2->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-104.0f, 9.0f, 5.0f);
	rot = 1.4f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree2->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-134.0f, 11.0f, 5.0f);
	rot = 1.8f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree2->OnRender(pd3dDevice);
		pos = D3DXVECTOR3(-144.0f, 16.0f, -15.0f);
	rot = 2.8f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree2->OnRender(pd3dDevice);

	//Treez 3
	m_Tree3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-50.0f, 0.0f, 20.0f);
	rot = 2.0f;
	m_Tree3->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-55.0f, 2.0f, -20.0f);
	rot = 1.0f;
	m_Tree3->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-55.0f, 2.0f, -32.0f);
	rot = 2.0f;
	m_Tree3->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-75.0f, 4.0f, -42.0f);
	rot = 2.0f;
	m_Tree3->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-85.0f, 6.0f, -62.0f);
	rot = 2.0f;
	m_Tree3->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree3->OnRender(pd3dDevice);
	pos = D3DXVECTOR3(-95.0f, 8.0f, -22.0f);
	rot = 2.0f;
	m_Tree3->OnFrameMove(pd3dDevice, rot, pos);
	m_Tree3->OnRender(pd3dDevice);

	m_Forest->OnRender(pd3dDevice);
}

/**
 * \brief Positions all models arround the terrain forming scenography.
 * Also animates moveable objects (if any)
 * \author Petar Bajic
 * \date January, 16 2007.
 */
void CTerrain::OnFrameMove(LPDIRECT3DDEVICE9 pd3dDevice, double fTime, float fElapsedTime, bool details)
{
	//Positioning menu 3D scenography
	D3DXVECTOR3 pos = D3DXVECTOR3(-1.0f, -2.0f, -5.0f);
	float rot = 3.5f;
	m_Base->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	rot = 0;
	m_Sky->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	rot = (float)fTime/30;
	m_Clouds->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(15.0f, -1.6f, -50.0f);
	rot = 0.0f;
	m_Fence->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(190.0f, 0.0f, -60.0f);
	rot = 3.0f;
	m_Hill1->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(0.0f, -10.0f, 160.0f);
	rot = 3.0f;
	m_Hill2->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(0.0f, -60.0f, -340.0f);
	rot = 0.0f;
	m_Hill3->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(50.0f, 2.0f, -5.0f);
	rot = 0.0f;
	m_Barrel->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(10.0f, 0.0f, -65.0f);
	rot = 0.0f;
	m_Trough->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(0.0f, 2.0f, 44.0f);
	rot = 2.0f;
	m_Shed->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(50.0f, -5.0f, 6.0f);
	rot = 0.0f;
	m_Tree1->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(-70.0f, 3.5f, 0.0f);
	rot = 0.0f;
	m_Tree2->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(-50.0f, 0.0f, 30.0f);
	rot = 0.0f;
	m_Tree3->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(60.0f, 6.0f, -80.0f);
	rot = 0.0f;
	m_House->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(-20.0f, 8.0f, -250.0f);
	rot = 0.0f;
	m_Barn->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(-70.0f, 7.0f, -100.0f);
	rot = 1.0f;
	m_Tractor->OnFrameMove(pd3dDevice, rot, pos);

	pos = D3DXVECTOR3(0.0f, -0.5f, 0.0f);
	rot = 0.0f;
	m_Grass2->OnFrameMove(pd3dDevice, rot, pos);

	if(details)
	{
		m_Grass->Update(fElapsedTime, false, false);
	}

	//Animate Bird Flying
	static float birdTime = -100.0f;
	static float height = 40.0f;
	static float direction = 0.0f;
	birdTime += fElapsedTime*20;
	if (birdTime > 100)
	{
		birdTime = -100.0f;
		height = (float) ((rand() % 30) + 30);// from 30 to 60;
		direction  = (float) (rand() % 628)/100; // 0 to 2Pi
	}

	D3DXMATRIXA16 matWorld,matTrans;
	D3DXMatrixIdentity(&matWorld);
	D3DXMatrixRotationY( &matWorld, direction);
	D3DXMatrixTranslation( &matTrans, birdTime*sin(direction), height, birdTime*cos(direction));
	D3DXMatrixMultiply(&matWorld, &matWorld, &matTrans);
	m_Bird->OnFrameMove(pd3dDevice, fElapsedTime, &matWorld);
}
