#include "Graphics2.h"

Graphics2 app;

void Graphics2::CreateSceneGraph()
{	
	_boostMultiplier = _boostMin;
	_flySpeed = 1;
	_turnSpeed = 1;
	_invertPitch = -1;

	_currentController = GamePadController();

	_currentPlayerObject = nullptr;

	GetCamera()->SetCameraPosition(0.0f, 550.0f, -80.0f);
	SceneGraphPointer sceneGraph = GetSceneGraph();

	// This is where you add nodes to the scene graph
	//shared_ptr<TexturedCubeNode> cube = make_shared<TexturedCubeNode>(L"Body", L"Textures\\woodbox.bmp");
	//cube->SetWorldTransform(XMMatrixScaling(5.0f, 8.0f, 2.5f) *  XMMatrixTranslation(0, 23.0f, 0)); XMMatrixScaling(50.0f, 800.0f, 50.0f) * XMMatrixTranslation(-65.0f, 350.0f, -65.0f)
	//sceneGraph->Add(cube);

	DirectXFramework::GetDXFramework()->GetGlobalLighting()->SetAmbientLight(XMFLOAT4(0.7f, 0.7f, 0.7f, 1.0f));
	DirectXFramework::GetDXFramework()->GetGlobalLighting()->SetDirectionalLight(XMVectorSet(0.5f, -1.0f, -1.0f, 0.0f), XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f));

	shared_ptr<SkyNode> skyDome = make_shared<SkyNode>(L"SkyDome", L"Textures\\SkyWater.dds", 180.0f);
	sceneGraph->Add(skyDome);
	
	//shared_ptr<HeightMapTerrainNode> terrainNode = make_shared<HeightMapTerrainNode>(L"MainTerrain", L"Textures\\Example_HeightMap.raw", L"RandomWords");
	//shared_ptr<PerlinTerrainNode> terrainNode = make_shared<PerlinTerrainNode>(L"MainTerrain", L"LovelyCat", 10.0f);
	shared_ptr<FractalTerrainNode> terrainNode = make_shared<FractalTerrainNode>(L"MainTerrain", L"LovelyCat", 10.0f);
	terrainNode->SetAmbientLight(DirectXFramework::GetDXFramework()->GetGlobalLighting()->GetAmbientLight());
	terrainNode->SetDirectionalLight(DirectXFramework::GetDXFramework()->GetGlobalLighting()->GetDirectionalLightDirection(), DirectXFramework::GetDXFramework()->GetGlobalLighting()->GetDirectionalLightColor());
	terrainNode->SetWaterColor(XMFLOAT4(SharedMethods::RGBValueToIntensity(0x84), SharedMethods::RGBValueToIntensity(0xC1), SharedMethods::RGBValueToIntensity(0xF9), 1.0f));
	//terrainNode->SetWaterColor(XMFLOAT4(1.0f, 0.0f, 0.8f, 1.0f));
	//terrainNode->SetWaterColor(XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f));
	sceneGraph->Add(terrainNode);

	shared_ptr<ControlledSplitMeshNode> plane1Node = make_shared<ControlledSplitMeshNode>(L"Plane1", L"Models\\Plane\\Bonanza.3DS");	
	plane1Node->SetStartOrientation(XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), XM_PI) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 0.5f * XM_PI));
	plane1Node->SetNodePosition(0.0f, 550.0f, -50.0f);	
	sceneGraph->Add(plane1Node);

	shared_ptr<SceneGraph> boatGraph = make_shared<SceneGraph>(L"boatGraph");
	boatGraph->SetWorldTransform(XMMatrixScaling(0.1f, 0.1f, 0.1f) * XMMatrixTranslation(-1880.0f, 290.0f, 610.0f));

	shared_ptr<ControlledMeshNode> boat1Node = make_shared<ControlledMeshNode>(L"Boat1", L"Models\\Boat\\Boat.FBX");
	boat1Node->SetStartOrientation(XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), (90.0f * XM_PI) / 180.0f) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), (90.0f * XM_PI) / 180.0f));
	boat1Node->SetNodePosition(0.0f, 0.0f, 0.0f);
	boatGraph->Add(boat1Node);

	sceneGraph->Add(boatGraph);

	_currentPlayerObject = plane1Node;
	GetCamera()->SetFollowNode(plane1Node, XMFLOAT3(0.0f, 40.0f, -100.0f), false);

	SetBackgroundColour(XMFLOAT4(0.29f, 0.38f, 0.72f, 1.0f));
	//SetBackgroundColour(XMFLOAT4(SharedMethods::RGBValueToIntensity(0x89), 0, 1, 1));

	_currentRotation = 0;
	_currentSideRotation = 0;
	_currentPropRotation = 0;
}

void Graphics2::GenerateClutter()
{
	SceneGraphPointer sceneGraph = GetSceneGraph();
	shared_ptr<TerrainNode> mainTerrain = dynamic_pointer_cast<TerrainNode>(sceneGraph->Find(L"MainTerrain"));

	if (!_initDone && mainTerrain != nullptr)
	{
		vector<TerrainPopNode> rngSpawnList;
		vector<TerrainPopNode> rngBushSpawnList;
		vector<TerrainPopNode> rngSnowSpawnList;
		vector<TerrainPopNode> rngDirtSpawnList;

		TerrainPopNode treeModelA = TerrainPopNode();
		treeModelA.modelName = L"Models\\Tree\\Tree1.fbx";
		treeModelA.nodeBaseName = L"treeA";
		treeModelA.scaleFactor = 0.5f;

		TerrainPopNode treeModelB = TerrainPopNode();
		treeModelB.modelName = L"Models\\Tree\\Tree2.fbx";
		treeModelB.nodeBaseName = L"treeB";
		treeModelB.scaleFactor = 0.5f;

		rngSpawnList.push_back(treeModelA);
		rngSpawnList.push_back(treeModelB);

		shared_ptr<SceneGraph> treeGroupA = make_shared<SceneGraph>(L"TreeGroupA");

		mainTerrain->PopulateTerrain(treeGroupA, rngSpawnList, 20, 20, 500.0f, 650.0f, 0.9f, 1.0f);
		treeGroupA->Initialise();
		sceneGraph->Add(treeGroupA);

		TerrainPopNode bushModelA = TerrainPopNode();
		bushModelA.modelName = L"Models\\Tree\\Bush1.fbx";
		bushModelA.nodeBaseName = L"bushA";
		bushModelA.scaleFactor = 0.05f;

		TerrainPopNode bushModelB = TerrainPopNode();
		bushModelB.modelName = L"Models\\Tree\\Bush2.fbx";
		bushModelB.nodeBaseName = L"bushB";
		bushModelB.scaleFactor = 0.05f;

		TerrainPopNode grassModelA = TerrainPopNode();
		grassModelA.modelName = L"Models\\Tree\\Grass1.fbx";
		grassModelA.nodeBaseName = L"grassA";
		grassModelA.scaleFactor = 0.005f;

		TerrainPopNode grassModelB = TerrainPopNode();
		grassModelB.modelName = L"Models\\Tree\\Grass2.fbx";
		grassModelB.nodeBaseName = L"grassB";
		grassModelB.scaleFactor = 0.05f;

		TerrainPopNode grassModelC = TerrainPopNode();
		grassModelC.modelName = L"Models\\Tree\\Grass3.fbx";
		grassModelC.nodeBaseName = L"grassC";
		grassModelC.scaleFactor = 0.05f;

		rngBushSpawnList.push_back(bushModelA);
		rngBushSpawnList.push_back(bushModelB);
		rngBushSpawnList.push_back(grassModelA);
		rngBushSpawnList.push_back(grassModelB);
		rngBushSpawnList.push_back(grassModelC);

		shared_ptr<SceneGraph> bushGroupA = make_shared<SceneGraph>(L"BushGroupA");

		mainTerrain->PopulateTerrain(bushGroupA, rngBushSpawnList, 7, 7, 500.0f, 650.0f, 0.95f, 1.0f);
		bushGroupA->Initialise();
		sceneGraph->Add(bushGroupA);

		TerrainPopNode treeModelSA = TerrainPopNode();
		treeModelSA.modelName = L"Models\\Tree\\Tree3.fbx";
		treeModelSA.nodeBaseName = L"treeSA";
		treeModelSA.scaleFactor = 0.1f;

		rngSnowSpawnList.push_back(treeModelSA);

		shared_ptr<SceneGraph> treeGroupSA = make_shared<SceneGraph>(L"TreeGroupSnowA");

		mainTerrain->PopulateTerrain(treeGroupSA, rngSnowSpawnList, 50, 50, 750.0f, 1000.0f, 0.6f, 1.0f);
		treeGroupSA->Initialise();
		sceneGraph->Add(treeGroupSA);

		TerrainPopNode treeModelDA = TerrainPopNode();
		treeModelDA.modelName = L"Models\\Tree\\Tree4.fbx";
		treeModelDA.nodeBaseName = L"treeDA";
		treeModelDA.scaleFactor = 0.05f;

		TerrainPopNode rockModelA = TerrainPopNode();
		rockModelA.modelName = L"Models\\Tree\\Rock1.fbx";
		rockModelA.nodeBaseName = L"rockA";
		rockModelA.scaleFactor = 0.1f;

		TerrainPopNode rockModelB = TerrainPopNode();
		rockModelB.modelName = L"Models\\Tree\\Rock2.fbx";
		rockModelB.nodeBaseName = L"rockB";
		rockModelB.scaleFactor = 0.1f;

		TerrainPopNode rockModelC = TerrainPopNode();
		rockModelC.modelName = L"Models\\Tree\\Rock3.fbx";
		rockModelC.nodeBaseName = L"rockC";
		rockModelC.scaleFactor = 0.1f;

		rngDirtSpawnList.push_back(treeModelDA);
		rngDirtSpawnList.push_back(rockModelA);
		rngDirtSpawnList.push_back(rockModelB);
		rngDirtSpawnList.push_back(rockModelC);

		shared_ptr<SceneGraph> treeGroupDA = make_shared<SceneGraph>(L"TreeGroupDirtA");

		mainTerrain->PopulateTerrain(treeGroupDA, rngDirtSpawnList, 10, 10, 310.0f, 410.0f, 0.9f, 1.0f);
		treeGroupDA->Initialise();
		sceneGraph->Add(treeGroupDA);

		_initDone = true;
	}
}

void Graphics2::UpdateSceneGraph()
{
	SceneGraphPointer sceneGraph = GetSceneGraph();
	shared_ptr<TerrainNode> mainTerrain = dynamic_pointer_cast<TerrainNode>(sceneGraph->Find(L"MainTerrain"));

	if (!_initDone && mainTerrain != nullptr)
	{
		//GenerateClutter();
	}

	GetCurrentControlInputs();
	XMVECTOR startCameraPos = GetCamera()->GetCameraPosition();
		
	if (_currentPlayerObject != nullptr)
	{
		_currentPlayerObject->SetForwardBack(_flySpeed * _boostMultiplier);
		startCameraPos = _currentPlayerObject->GetNodePosition();
	}

	for (ControlInputs currentControl : _currentInputs)
	{
		switch (currentControl)
		{
		case ControlInputs::Forward:
			if (_currentPlayerObject == nullptr)
			{
				GetCamera()->SetForwardBack(_flySpeed * _boostMultiplier);
			}
			break;
		case ControlInputs::Back:
			if (_currentPlayerObject == nullptr)
			{				
				GetCamera()->SetForwardBack(-_flySpeed * _boostMultiplier);
			}
			break;
		case ControlInputs::StrafeLeft:
			if (_currentPlayerObject != nullptr)
			{
				//_currentPlayerObject->SetLeftRight(-_flySpeed * _boostMultiplier);
				_currentPlayerObject->SetRoll(_turnSpeed * 0.5f);
			}
			else
			{
				GetCamera()->SetLeftRight(-_flySpeed * _boostMultiplier);
			}
			break;
		case ControlInputs::StrafeRight:
			if (_currentPlayerObject != nullptr)
			{
				//_currentPlayerObject->SetLeftRight(_flySpeed * _boostMultiplier);
				_currentPlayerObject->SetRoll(-_turnSpeed * 0.5f);
			}
			else
			{
				GetCamera()->SetLeftRight(_flySpeed * _boostMultiplier);
			}
			break;
		case ControlInputs::TurnLeft:
			if (_currentPlayerObject != nullptr)
			{
				_currentPlayerObject->SetYaw(-_turnSpeed);				
			}
			else
			{
				GetCamera()->SetYaw(-_turnSpeed);
			}
			break;
		case ControlInputs::TurnRight:
			if (_currentPlayerObject != nullptr)
			{
				_currentPlayerObject->SetYaw(_turnSpeed);				
			}
			else
			{
				GetCamera()->SetYaw(_turnSpeed);
			}
			break;
		case ControlInputs::Up:
			if (_currentPlayerObject != nullptr)
			{
				_currentPlayerObject->SetPitch(_turnSpeed * _invertPitch);
			}
			else
			{
				GetCamera()->SetPitch(_turnSpeed * _invertPitch);
			}
			break;
		case ControlInputs::Down:
			if (_currentPlayerObject != nullptr)
			{
				_currentPlayerObject->SetPitch(-_turnSpeed * _invertPitch);
			}
			else
			{
				GetCamera()->SetPitch(-_turnSpeed * _invertPitch);
			}
			break;
		}
	}

	shared_ptr<ControlledSplitMeshNode> plane1 = dynamic_pointer_cast<ControlledSplitMeshNode>(sceneGraph->Find(L"Plane1"));
	shared_ptr<ControlledMeshNode> boat1 = dynamic_pointer_cast<ControlledMeshNode>(sceneGraph->Find(L"Boat1"));

	if (plane1 != nullptr)
	{
		//plane1->SetWorldTransform(SharedMethods::RotateFromPoint(-60.0f, 0, 0, XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), _currentRotation * XM_PI / 180.0f)));
		//plane1->AddToWorldTransform(XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 90.0f * XM_PI / 180.0f));
		//sceneGraph->Find(L"Plane1")->GetFirstChild()->GetFirstChild()->SetWorldTransform(XMMatrixRotationAxis(XMVectorSet(0.0f, -1.0f, -1.0f, 0.0f), _currentSideRotation * XM_PI / 180.0f));

		//sceneGraph->Find(L"Plane1")->Update((SharedMethods::RotateFromPoint(30.0f, -20.0f, 0, XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), _currentSideRotation * XM_PI / 180.0f))) * XMMatrixRotationAxis(XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f), _currentRotation * XM_PI / 180.0f));				
		plane1->Find(L"airscrew")->SetWorldTransform(SharedMethods::RotateFromPoint(0.0f, 15.471f, 14.5f, XMMatrixRotationAxis(XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), _currentPropRotation * XM_PI / 180.0f)));
	}

	if (boat1 != nullptr)
	{
		boat1->SetForwardBack(-3.0f);
		boat1->SetYaw(-0.2f);
	}

	if (_currentRotation == 360)
	{
		_currentRotation = 0;
		_currentSideRotation = 0;
		_currentPropRotation = 0;
	}
	else
	{
		_currentRotation += 1;
		_currentSideRotation += 1;		
		_currentPropRotation += 10 * _boostMultiplier;
		
	}	

	// Check the camera and our terrain boundaries
	XMVECTOR currentCameraPos = GetCamera()->GetCameraPosition();	
	if (_currentPlayerObject != nullptr)
	{
		currentCameraPos = _currentPlayerObject->GetNodePosition();
	}
	GetCamera()->Update();
	if (mainTerrain != nullptr)
	{
		bool boundaryHit = false;
		XMFLOAT4 cameraOffset = XMFLOAT4(XMVectorGetX(startCameraPos), XMVectorGetY(startCameraPos), XMVectorGetZ(startCameraPos), 0.0f);

		if (XMVectorGetY(currentCameraPos) < mainTerrain->GetHeightAtPoint(XMVectorGetX(currentCameraPos), XMVectorGetZ(currentCameraPos), true) + 4.0f)
		{
			cameraOffset.y = cameraOffset.y + 4.0f;
			boundaryHit = true;
		}

		float currentCameraXPos = XMVectorGetX(currentCameraPos);
		float xOffset = 150.0f;
		if (currentCameraXPos > 0)
		{
			xOffset = -xOffset;
		}

		float currentCameraZPos = XMVectorGetZ(currentCameraPos);
		float zOffset = 150.0f;
		if (currentCameraZPos > 0)
		{
			zOffset = -zOffset;
		}

		if (!mainTerrain->CheckXBoundary(currentCameraXPos + -xOffset))
		{
			cameraOffset.x = cameraOffset.x + xOffset;
			boundaryHit = true;
		}

		if (!mainTerrain->CheckZBoundary(currentCameraZPos + -zOffset))
		{
			cameraOffset.z = cameraOffset.z + zOffset;
			boundaryHit = true;
		}

		if (boundaryHit)
		{
			if (_currentPlayerObject != nullptr)
			{
				_currentPlayerObject->SetNodePosition(cameraOffset);
			}
			else
			{
				GetCamera()->SetCameraPosition(cameraOffset);
			}
		}
	}

	// Reset the control array ready for the next frames input
	ResetCurrentControlInputs();
}

void Graphics2::GetCurrentControlInputs()
{
	// Check if the window has focus before accepting any keypresses
	if (GetForegroundWindow() == Framework::GetHWnd())
	{

		// Check if any connected controllers have inputs
		bool boostHit = false;
		_currentController.ProcessGameController(_currentInputs, boostHit);

		if (GetAsyncKeyState(VK_SHIFT) || boostHit)
		{
			_currentInputs.insert(ControlInputs::Fire1);
			_boostMultiplier = SharedMethods::Clamp<float>(_boostMultiplier + _boostStep, 1, _boostMax);			
		}
		else
		{
			_boostMultiplier = SharedMethods::Clamp<float>(_boostMultiplier - _boostStep, 1, _boostMax);
		}

		// Forward
		if (GetAsyncKeyState(0x57))
		{
			_currentInputs.insert(ControlInputs::Forward);
		}

		// Back
		if (GetAsyncKeyState(0x53))
		{
			_currentInputs.insert(ControlInputs::Back);
		}

		// Turn Left
		if (GetAsyncKeyState(0x41))
		{
			_currentInputs.insert(ControlInputs::TurnLeft);
		}

		// Turn Right
		if (GetAsyncKeyState(0x44))
		{
			_currentInputs.insert(ControlInputs::TurnRight);
		}

		// Strafe Left
		if (GetAsyncKeyState(0x51))
		{
			_currentInputs.insert(ControlInputs::StrafeLeft);
		}

		// Strafe Right
		if (GetAsyncKeyState(0x45))
		{
			_currentInputs.insert(ControlInputs::StrafeRight);
		}

		// "Jump"
		if (GetAsyncKeyState(VK_SPACE))
		{
			_currentInputs.insert(ControlInputs::Up);
		}

		// "Crouch"
		if (GetAsyncKeyState(VK_CONTROL))
		{
			_currentInputs.insert(ControlInputs::Down);
		}
	}
}

void Graphics2::ResetCurrentControlInputs()
{
	_currentInputs.clear();
}