//!
/**
 **************************************************************************************************************************************************************
 * Bot-Strike GB renderer
 **************************************************************************************************************************************************************
 * @file   Render.cpp
 * @author Guillaume Blanchard
 **************************************************************************************************************************************************************/
#pragma once

/**************************************************************************************************************************************************************
 * Includes
 **************************************************************************************************************************************************************/

// BotStrike
#include "Render.h"

namespace BS {

/**************************************************************************************************************************************************************
 * Implementation
 **************************************************************************************************************************************************************/

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * BG Render class constructor
 */
CGBRender::CGBRender(): m_ObjectCounter(0)
{
	m_Flag.Value = 0;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Initialize GB render
 */
void CGBRender::Initialize()
{
	if(m_Flag.Initialized == 1)
		return;

	/// first camera creation.
	m_Camera.WorldView().Identity();
	m_Camera.SetMode(noe::Camera::TARGET);
	m_Camera.Target().Clear();
	m_Camera.Position().Set(0.0f, 50.0f, 10.0f);
	m_Camera.Process();

	/// scene creation.
	noe::SceneInstance.AddView(&m_Camera);

	/// window's setting.
	noe::WindowConfig wc;
	wc.name         = "BotStrike - RendererBG";
	wc.position.x   = 100;
	wc.position.y   = 100;
	wc.size.x       = 800;
	wc.size.y       = 600;
	wc.fullscreen   = FALSE;
	wc.frame_buffer = 32;
	wc.z_buffer     = 16;
	wc.multi_sample = 1;
	wc.sync         = TRUE;

	/// window creation. 
	noe::WindowInstance.Create(&wc);

	/// input creation
	noe::InputInstance.Initialize();

	/// Root task initialization
	//noe::ITaskRoot.Initialize();

	// Initializ
	noe::SceneInstance.AddFont(&noe::Font::Default());

	// Background fielde default font
	noe::File bg_file("Data/Texture/gcheck.tga");
	m_Field.Texture.Load(&bg_file);
	m_Field.Material.resize(1);
	m_Field.Material[0].SetTexture(0, &m_Field.Texture);
	m_Field.ModelList.resize(1);
	m_Field.ModelList[0].CreatePlane(100.0f, 100.0f, &m_Field.Material[0], noe::Model::MODEL_HORIZONTAL, 0xFFFFFFFF, 10.0f, 10.0f);
	m_Field.ModelList[0].SetCastShadow(TRUE);
	noe::SceneInstance.AddModel(&m_Field.ModelList[0]);

	m_Flag.Initialized = 1;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Initialize GB render
 */
void CGBRender::Release()
{
	if(m_Flag.Initialized == 0)
		return;

	/// Stop render thread.
	noe::RenderInstance.Wait(TRUE); // Wait for rendering thread
	noe::RenderInstance.Stop();

	/// scene creation.
	//noe::SceneInstance.Release();

	/// window creation. 
	//noe::WindowInstance.Release();

	m_Flag.Initialized = 0;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Create arena data
 * @param	arena	Input arena	data
 */
void CGBRender::CreateArena(const BS::CArena& arena)
{
	if(m_Flag.Initialized == 0) // not yet initialized
		return;

	if(m_Flag.CreateArena == 1) // arena already created
		return;

	// compute wall number
	u32 wallNum(0);
    loop(y, arena.height)
        loop(x, arena.width)
			if(arena.cell(x,y) & BS::ECell::Box)
				wallNum++;

	// Create texture & material
	noe::File ar_file("Data/Texture/briques.jpg");
	m_Walls.Texture.Load(&ar_file);
	m_Walls.Material.resize(1);
	m_Walls.Material[0].SetTexture(0, &m_Walls.Texture);
	
	m_Walls.ModelList.clear();
	m_Walls.ModelList.resize(wallNum + (arena.width * 2) + (arena.height * 2));

	i32 halfWidth = arena.width >> 1;
	i32 halfHeight = arena.height >> 1;

	// Create wall models
	wallNum = 0;
    loop(y, arena.height)
	{
        loop(x, arena.width)
		{
			if(arena.cell(x, y) & BS::ECell::Box)
			{
				m_Walls.ModelList[wallNum].CreateCube(1.0f, &m_Walls.Material[0], 0xFFFFFFFF);
				m_Walls.ModelList[wallNum].SetPosition(scast<f32>(x - halfWidth), 0.5f, scast<f32>(y - halfHeight));
				m_Walls.ModelList[wallNum].SetCastShadow(TRUE);
				noe::SceneInstance.AddModel(&m_Walls.ModelList[wallNum]);
				wallNum++;
			}
		}
	}

	// Borders
	loop(i, 2)
	{
		i32 x = (i == 0) ? -1 : arena.width;
		loop(y, arena.height)
		{
			m_Walls.ModelList[wallNum].CreateCube(1.0f, &m_Walls.Material[0], 0xFFFFFFFF);
			m_Walls.ModelList[wallNum].SetPosition(scast<f32>(x - halfWidth), 0.5f, scast<f32>(y - halfHeight));
			m_Walls.ModelList[wallNum].SetCastShadow(TRUE);
			noe::SceneInstance.AddModel(&m_Walls.ModelList[wallNum]);
			wallNum++;
		}
	}

	// Borders
	loop(i, 2)
	{
		i32 y = (i == 0) ? -1 : arena.height;
		loop(x, arena.width)
		{
			m_Walls.ModelList[wallNum].CreateCube(1.0f, &m_Walls.Material[0], 0xFFFFFFFF);
			m_Walls.ModelList[wallNum].SetPosition(scast<f32>(x - halfWidth), 0.5f, scast<f32>(y - halfHeight));
			m_Walls.ModelList[wallNum].SetCastShadow(TRUE);
			noe::SceneInstance.AddModel(&m_Walls.ModelList[wallNum]);
			wallNum++;
		}
	}

	// Create objects
	noe::File obj_file("Data/Texture/smiley.png");
	m_Objects.Texture.Load(&obj_file);
	m_Objects.Material.resize(OBJECT_MAX);
	m_Objects.ModelList.resize(OBJECT_MAX);
	loop(i, OBJECT_MAX)
	{
		m_Objects.Material[i].SetTexture(0, &m_Objects.Texture);
		m_Objects.ModelList[i].CreateSphere(0.5f, 16, &m_Objects.Material[i], 0xFFFFFFFF);
		m_Objects.ModelList[i].SetCastShadow(TRUE);
		m_Objects.ModelList[i].SetActive(FALSE);
		noe::SceneInstance.AddModel(&m_Objects.ModelList[i]);
	}

	m_Flag.CreateArena = 1;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Get camera reference
 * @return		Reference to renderer camera
 */
noe::Camera& CGBRender::GetCamera() { return m_Camera; }

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Get camera reference
 * @param	x		Grid x coordinate
 * @param	y		Grid y coordinate
 * @param	arena	Input arena	data
 * @return			Coordinate in the world space
 */
noe::Vector3 CGBRender::GridToWorld(i32 x, i32 y, const BS::CArena& arena)
{ 
	return noe::Vector3(scast<f32>(x - (arena.width >> 1)), 0.5f, scast<f32>(y - (arena.height >> 1)));
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Reset dynamic objects
 */
void CGBRender::ResetObjects()
{ 
	if(m_Flag.CreateArena == 0) // arena not yet created
		return;

	m_ObjectCounter = 0;
	loop(i, OBJECT_MAX)
		m_Objects.ModelList[i].SetActive(FALSE);
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Add new dynamic object
 * @param	type	Object type. @see BS::ECell
 * @param	x		Grid x coordinate
 * @param	y		Grid y coordinate
 * @param	arena	Input arena	data
 * @return			TRUE if an object is available
 */
BOOL CGBRender::AddObject(i32 type, i32 x, i32 y, const BS::CArena& arena)
{
	if(m_Flag.CreateArena == 0) // arena not yet created
		return FALSE;

	if(m_ObjectCounter >= scast<i32>(m_Objects.ModelList.size())) // object list is full
		return FALSE;

	f32 scale(1.f);
	noe::Vector4 color(1.f, 1.f, 1.f, 1.f);
	switch(type)
	{
		case BS::ECell::Cadavre: color = noe::Vector4(0.5f, 0.5f, 0.5f, 1.f); break;
		case BS::ECell::Grenade: color = noe::Vector4(1.f, 0.f, 1.f, 1.f); ; scale = 0.5f; break;
		case BS::ECell::Bullet:  color = noe::Vector4(1.f, 1.f, 0.f, 1.f); ; scale = 0.5f; break;
		case BS::ECell::Blue:    color = noe::Vector4(1.f, 0.f, 0.f, 1.f); ; break;
		case BS::ECell::Red:     color = noe::Vector4(0.f, 0.f, 1.f, 1.f); ; break;
	}

	noe::Model& model = m_Objects.ModelList[m_ObjectCounter++];
	model.SetActive(TRUE);
	model.SetPosition(GridToWorld(x, y, arena));
	model.SetScale(scale);
	noe::MaterialGroup* matGroup = model.GetMaterialGroup(0);
	if(matGroup && matGroup->GetMaterial())
		matGroup->GetMaterial()->SetDiffuse(color);

	return TRUE;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Update renderer
 */
void CGBRender::Update()
{
	//noe::ITaskRoot.Update();

	// Free camera
	#if 0
	{
		m_Camera.SetMode(noe::Camera::FREE);

		static const f32 ANGULAR = 0.05f;
		f32 SPEED = 0.2f;
		if(noe::InputInstance.IsPress(noe::TRIGGER_L_1))
			SPEED = 0.5f;

		static f32 yaw = 0.0f;
		static f32 pitch = 0.0f;

		if(noe::InputInstance.IsPress(noe::CROSS_L_RIGHT))
			yaw += ANGULAR;
		else if(noe::InputInstance.IsPress(noe::CROSS_L_LEFT))
			yaw -= ANGULAR;

		if(noe::InputInstance.IsPress(noe::CROSS_L_UP))
			pitch += ANGULAR;
		else if(noe::InputInstance.IsPress(noe::CROSS_L_DOWN))
			pitch -= ANGULAR;

		// Direction update
		noe::Bound(pitch, -noe::PI_2*0.9f, noe::PI_2*0.9f);
		m_Camera.Direction().x = sinf(yaw) * cosf(pitch);
		m_Camera.Direction().y = sinf(pitch);
		m_Camera.Direction().z = cosf(yaw) * cosf(pitch);
		m_Camera.Direction().Normalize();

		if(noe::InputInstance.IsPress(noe::BUTTON_6)) // backward
			m_Camera.Position() += m_Camera.Direction() * SPEED;
		else if(noe::InputInstance.IsPress(noe::BUTTON_10)) // forward
			m_Camera.Position() -= m_Camera.Direction() * SPEED;

		if(noe::InputInstance.IsPress(noe::BUTTON_9)) // right
		{
			noe::Vector3 tan = m_Camera.Direction() ^ noe::Vector3::Up();
			tan.Normalize();
			m_Camera.Position() += tan * SPEED;
		}
		else if(noe::InputInstance.IsPress(noe::BUTTON_11)) // left
		{
			noe::Vector3 tan = m_Camera.Direction() ^ noe::Vector3::Up();
			tan.Normalize();
			m_Camera.Position() -= tan * SPEED;
		}

		m_Camera.Process();
	}
	#endif

	noe::InputInstance.Process();		
	noe::WindowInstance.Process();
}

/**************************************************************************************************************************************************************/

} // namespace BS