#include "../Fusion/RuFusion_Minimap.h"

#pragma unmanaged

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

ruRTTI_DEFINE_SUBCLASS(CRuFusion_Minimap, CRuFusion_UIElement)
ruHEAP_DEFINE_SUBCLASS(CRuFusion_Minimap, "CRuFusion_Minimap", "CRuFusion_UIElement")
ruCLASSGUID_DEFINE(CRuFusion_Minimap, 0x17F75534, 0xAE1540ea, 0xBB660D81, 0x21BEF9AB)

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

CRuFusion_Minimap::CRuFusion_Minimap()
:	m_worldName(NULL),
	m_zoom(1.0f),
	m_minimapMask(NULL)
{
}

CRuFusion_Minimap::~CRuFusion_Minimap()
{
	delete[] m_worldName;

	ruSAFE_RELEASE(m_minimapMask);
}

void CRuFusion_Minimap::SetWorldProperties(const char *worldName, const CRuAABB &worldBounds)
{
	// Copy world name
	delete[] m_worldName;
	m_worldName = ruNEW char [strlen(worldName) + 1];
	strcpy(m_worldName, worldName);

	// Copy world bounds
	m_worldBounds = worldBounds;
}

void CRuFusion_Minimap::SetMinimapViewport(const CRuRectangle &viewport)
{
	m_minimapViewport = viewport;
}

void CRuFusion_Minimap::SetMinimapOrigin(const CRuVector2 &origin)
{
	m_minimapOrigin = origin;
}

void CRuFusion_Minimap::SetPlayerPosition(const CRuVector3 &position)
{
	m_playerPosition = position;
}

void CRuFusion_Minimap::SetPlayerDir(const CRuVector3 &dir)
{
}

void CRuFusion_Minimap::SetZoom(REAL zoom)
{
	m_zoom = zoom;
}

void CRuFusion_Minimap::SetMinimapMask(IRuTexture *minimapMask)
{
	if(minimapMask)
	{
		minimapMask->AddRef();
		ruSAFE_RELEASE(m_minimapMask);
		m_minimapMask = minimapMask;
	}
}

void CRuFusion_Minimap::SetArrow(IRuTexture *minimapMask)
{
}

CRuVector3 CRuFusion_Minimap::WorldToMinimap(const CRuVector3 &position)
{
	CRuVector2 viewportDimension(m_minimapViewport.m_right - m_minimapViewport.m_left, m_minimapViewport.m_bottom - m_minimapViewport.m_top);
	CRuVector2 minimapOrigin(m_minimapOrigin);
	CRuVector2 minimapCenter(viewportDimension.x / 2.0f, viewportDimension.y / 2.0f);
	CRuVector2 minimapOriginOffset = minimapCenter - minimapOrigin;

	// Scale viewport dimension by zoom factor
	viewportDimension = viewportDimension / m_zoom;
	minimapOrigin = minimapOrigin / m_zoom;
	minimapCenter = minimapCenter / m_zoom;
	minimapOriginOffset = minimapOriginOffset / m_zoom;

	// Convert viewport dimensions to minimap coordinates
	CRuVector2 viewportDimensionMM = viewportDimension / m_texelScale;
	CRuVector2 viewportDimensionW = viewportDimensionMM * m_worldScale;

	CRuVector2 minimapOriginMM = minimapOrigin / m_texelScale;
	CRuVector2 minimapOriginW = minimapOriginMM * m_worldScale;

	CRuVector2 minimapCenterW = (minimapCenter / m_texelScale) * m_worldScale;
	CRuVector2 minimapOriginOffsetW = (minimapOriginOffset / m_texelScale) * m_worldScale;

	CRuRectangle viewportW;
	viewportW.m_left = m_playerPosition.x - minimapCenterW.x + minimapOriginOffsetW.x;
	viewportW.m_top = m_playerPosition.z - minimapCenterW.y - minimapOriginOffsetW.y;
	viewportW.m_right = viewportW.m_left + viewportDimensionW.x;
	viewportW.m_bottom = viewportW.m_top + viewportDimensionW.y;

	return CRuVector3	(
							(position.x - viewportW.m_left) / m_worldScale * (m_texelScale * m_zoom) + m_minimapViewport.m_left,
							-(position.z - viewportW.m_bottom) / m_worldScale * (m_texelScale * m_zoom) - (m_texelScale * m_zoom) + m_minimapViewport.m_top,
							0.0f
						);
}

void CRuFusion_Minimap::Display()
{
	Ru2D_EffectType displayEffect = ru2DFXTYPE_BLIT_FILTER;

	if(m_minimapMask)
	{
		displayEffect = ru2DFXTYPE_BLIT_MASKED;

		// Draw minimap mask
		g_ruSceneRenderer->Ru2D_SetEffectType(ru2DFXTYPE_BLIT_MASK);
		g_ruSceneRenderer->Ru2D_SetEffectParameter(ru2DFXPARAM_ROTATE, ru2DROTATE_NONE);
		g_ruSceneRenderer->Ru2D_SetTexture(m_minimapMask);

		CRuRectangle srcRect;

		srcRect.m_left = 0.0f;
		srcRect.m_top = 0.0f;
		srcRect.m_right = static_cast<REAL>(m_minimapMask->GetLevelWidth(0));
		srcRect.m_bottom = static_cast<REAL>(m_minimapMask->GetLevelHeight(0));

		g_ruSceneRenderer->Ru2D_Submit(srcRect, m_minimapViewport);
	}

	char *m_minimapPath = "texture\\minimap";

	/*
		NOTES
		-----
		There is much zen in this coordinate conversion code. So much that even I cannot
		understand exactly what it does to accomplish the task. But it does work, so
		that's all that really matters.
	*/

	m_texelScale = 256.0f;
	m_worldScale = 480.0f * 8.0f;

	CRuVector2 viewportDimension(m_minimapViewport.m_right - m_minimapViewport.m_left, m_minimapViewport.m_bottom - m_minimapViewport.m_top);
	CRuVector2 minimapOrigin(m_minimapOrigin);
	CRuVector2 minimapCenter(viewportDimension.x / 2.0f, viewportDimension.y / 2.0f);
	CRuVector2 minimapOriginOffset = minimapCenter - minimapOrigin;

	// Scale viewport dimension by zoom factor
	viewportDimension = viewportDimension / m_zoom;
	minimapOrigin = minimapOrigin / m_zoom;
	minimapCenter = minimapCenter / m_zoom;
	minimapOriginOffset = minimapOriginOffset / m_zoom;

	// Convert viewport dimensions to minimap coordinates
	CRuVector2 viewportDimensionMM = viewportDimension / m_texelScale;
	CRuVector2 viewportDimensionW = viewportDimensionMM * m_worldScale;

	CRuVector2 minimapOriginMM = minimapOrigin / m_texelScale;
	CRuVector2 minimapOriginW = minimapOriginMM * m_worldScale;

	CRuVector2 minimapCenterW = (minimapCenter / m_texelScale) * m_worldScale;
	CRuVector2 minimapOriginOffsetW = (minimapOriginOffset / m_texelScale) * m_worldScale;

	CRuRectangle viewportW;
	viewportW.m_left = m_playerPosition.x - minimapCenterW.x + minimapOriginOffsetW.x;
	viewportW.m_top = m_playerPosition.z - minimapCenterW.y - minimapOriginOffsetW.y;
	viewportW.m_right = viewportW.m_left + viewportDimensionW.x;
	viewportW.m_bottom = viewportW.m_top + viewportDimensionW.y;

	CRuRectangle viewportM;
	viewportM.m_left = (viewportW.m_left - m_worldBounds.Minima().x) / m_worldScale;
	viewportM.m_right = (viewportW.m_right - m_worldBounds.Minima().x) / m_worldScale;
	viewportM.m_top = (viewportW.m_top - m_worldBounds.Minima().z) / m_worldScale;
	viewportM.m_bottom = (viewportW.m_bottom - m_worldBounds.Minima().z) / m_worldScale;

	char textureName[2048];

	for(INT32 z = static_cast<INT32>(floor(viewportM.m_top)); z <= ceil(viewportM.m_bottom); ++z)
	{
		for(INT32 x = static_cast<INT32>(floor(viewportM.m_left)); x <= ceil(viewportM.m_right); ++x)
		{
			// Convert from minimap coordinates to viewport coordinates
			CRuRectangle targetRect;
			targetRect.m_left = (((x * m_worldScale) + m_worldBounds.Minima().x) - viewportW.m_left) / m_worldScale * (m_texelScale * m_zoom) + m_minimapViewport.m_left;
			targetRect.m_left = floor(targetRect.m_left);

			targetRect.m_right = targetRect.m_left + (m_texelScale * m_zoom) + 1.0f;

			targetRect.m_top = -(((z * m_worldScale) + m_worldBounds.Minima().z) - viewportW.m_bottom) / m_worldScale * (m_texelScale * m_zoom) - (m_texelScale * m_zoom) + m_minimapViewport.m_top;
			targetRect.m_top = floor(targetRect.m_top);

			targetRect.m_bottom = targetRect.m_top + (m_texelScale * m_zoom) + 1.0f;

			sprintf(textureName, "%s\\minimap_%d_%d", m_minimapPath, x, z);

			IRuTexture *testTexture = g_ruResourceManager->LoadTexture(textureName);

			if(testTexture)
			{
				CRuRectangle srcRect, destRect;

				srcRect.m_left = 0.0f;
				srcRect.m_top = 0.0f;
				srcRect.m_right = static_cast<REAL>(testTexture->GetLevelWidth(0));
				srcRect.m_bottom = static_cast<REAL>(testTexture->GetLevelHeight(0));

				destRect = targetRect;

				g_ruSceneRenderer->Ru2D_SetEffectType(displayEffect);
				g_ruSceneRenderer->Ru2D_SetTexture(testTexture);
				g_ruSceneRenderer->Ru2D_Submit(srcRect, destRect);

				ruSAFE_RELEASE(testTexture);
			}
		}
	}
}

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

#pragma managed
