//---------------------------------------------------------------------------------------------------------
//	AVP_Attractors_System.cpp
//	Created by GBT - 15 March 2007
//	Copyright (C) 2007 Rebellion, All Rights Reserved.
//---------------------------------------------------------------------------------------------------------

// Includes
#include "Core/Asura.h"

//This file's header
#include "Attractors/AVP_Attractors_System.h"

// Asura
#include "Attractors/Asura_Attractor_System.h"
#include "Attractors/Asura_Attractor_Compiled.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Core/Asura_HashTree.h"
#include "Core/Asura_Timers.h"
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Debugging/Asura_DebugLineRender.h"
#include "Rendering/Asura_Font_System.h"
#endif

// AVP
#include "Blueprint/AVP_Blueprint_HashIDs.h"

#ifdef ASURA_TOOLS
#include "Attractors/Tools_Attractor.h"
#include "Attractors/Tools_Attractor_Node.h"
#include "Attractors/Tools_Object_Attractor_Node.h"
#endif

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Attractors_System
//
//////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_TOOLS
float AVP_Attractors_System::s_fAttractorTypeUpdateTimer = 0.0f;
bool  AVP_Attractors_System::s_bToolsInitialised         = false;
#endif //#ifdef ASURA_TOOLS

const Asura_Angle                                    AVP_Attractors_System::s_fMaxAngleToEnvHook = Asura_Maths::Deg180/3.0f;
Asura_TemplateHashTree< AVP_Attractor_ProjectData* > AVP_Attractors_System::s_xProjectDataTree;

void AVP_Attractor_ProjectData::Initialise( const Asura_Attractor_Compiled& xAttractor )
{
    // Find our blueprint from the env hooks blueprints.
    Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_ENV_HOOKS, xAttractor.GetID() );
    if ( pxBlueprint )
    {
        InitialiseFromBlueprint( *pxBlueprint );
    }
}

void AVP_Attractor_ProjectData::InitialiseFromBlueprint( Asura_Blueprint& xBlueprint )
{

}

void AVP_Attractors_System::Initialise()
{
    // Create all of the project specific data from the Asura attractor system.
    for ( int i = 0; i < Asura_Attractor_System::GetInstance()->GetNumAttractors(); ++i )
    {
        const Asura_Attractor_Compiled& xAttractor = *Asura_Attractor_System::GetInstance()->GetAttractor( i );

        AVP_Attractor_ProjectData* pxProjectData = new AVP_Attractor_ProjectData();
        pxProjectData->Initialise( xAttractor );
        s_xProjectDataTree.AddNode( xAttractor.GetID(), pxProjectData );
    }
}

void AVP_Attractors_System::Shutdown()
{
    s_xProjectDataTree.Reset();
}

bool AVP_Attractors_System::GetHorizontalAttractorBestPosition( const Asura_Vector_3& xPosition, const Asura_Attractor_Compiled& xEnvHook, Asura_Vector_3& xBestPosition )
{
    Asura_Vector_3 xNodeZeroPos			  = xEnvHook.GetNode(0)->GetPosition();
    Asura_Vector_3 xNodeOnePos			  = xEnvHook.GetNode(1)->GetPosition();
    Asura_Vector_3 xNodeOneToNodeZeroDiff = xNodeOnePos - xNodeZeroPos;

    float U =  (((xPosition.x - xNodeZeroPos.x) * (xNodeOnePos.x - xNodeZeroPos.x)) + 
                ((xPosition.y - xNodeZeroPos.y) * (xNodeOnePos.y - xNodeZeroPos.y)) + 
                ((xPosition.z - xNodeZeroPos.z) * (xNodeOnePos.z - xNodeZeroPos.z)))
                / xNodeOneToNodeZeroDiff.MagnitudeSquared();

    if ((U < 0.0f) || (U > 1.0f))
    {
        // Closest Point does not fall withing the line segment
        return false;
    }

    xBestPosition.x = xNodeZeroPos.x + U * (xNodeOnePos.x - xNodeZeroPos.x);
    xBestPosition.y = xNodeZeroPos.y + U * (xNodeOnePos.y - xNodeZeroPos.y);
    xBestPosition.z = xNodeZeroPos.z + U * (xNodeOnePos.z - xNodeZeroPos.z);

    return true;
}

AVP_Attractor_ProjectData* AVP_Attractors_System::GetAttractorProjectData( const Asura_Attractor_Compiled& xEnvHook )
{
    return *s_xProjectDataTree.GetDataByHash( xEnvHook.GetID() );
}

#ifdef ASURA_TOOLS
void AVP_Attractors_System::Tools_Initialise()
{
    if ( !s_bToolsInitialised )
	{
        UpdateAttractorTypes();
		s_bToolsInitialised = true;
	}
}

void AVP_Attractors_System::Tools_Update()
{
    // We need to check that the attractor types are up-to-date, in case people create new blueprints.
    s_fAttractorTypeUpdateTimer += Asura_Timers::GetGameTimeSlice();
    if ( s_fAttractorTypeUpdateTimer > 1.0f )
    {
        s_fAttractorTypeUpdateTimer = 0.0f;
        UpdateAttractorTypes();
    }
}

void AVP_Attractors_System::Tools_Render()
{
	Tools_Object_Attractor_Node::NODE_LIST_IT xListIt(Tools_Object_Attractor_Node::GetList());

	for ( ; !xListIt.done(); xListIt.next() )
	{
		Tools_Object_Attractor_Node* pxCurrent = xListIt();
		if ( pxCurrent && pxCurrent->GetNode() )
		{
			const Tools_Attractor* const pxAttractor = pxCurrent->GetNode()->GetAttractor();
			Asura_Assert( pxAttractor, "Cant get env hook from node. Error" );
			if ( pxAttractor && pxAttractor->GetType() == uAVP_ATTRACTOR_TYPE_ALIEN_LEAP )
			{
				//only render lines for nodes with selected tools objects
				if(!pxAttractor->AreAnyNodesOrTheirRootObjectsSelected()) continue;
				
				// get next and last
				const Tools_Attractor_Node* pxNext = pxCurrent->GetNode()->GetNext();

				if ( ( !pxNext ) && ( pxAttractor->IsClosed() ) )
				{
					pxNext = pxAttractor->GetFirstNode();
				}

				if ( pxNext )
				{
					const Asura_Vector_3 xPos0 = pxCurrent->GetNode()->GetPosition();
					const Asura_Vector_3 xPos1 = pxNext->GetPosition();

					Asura_Bounding_Box xBoundingBox;
					xBoundingBox.SetToPoint( xPos0);
					xBoundingBox.Set( xPos1);

					if( true == Asura_Camera::IsAABBInFrustum( xBoundingBox))
					{
						Asura_Vector_3 xPosDir = ( xPos1 - xPos0 );
						xPosDir.Normalise();
						Asura_Vector_3 xNormal;
						pxAttractor->GetNormalFromDirection(xNormal, xPosDir);

						const Asura_Vector_3 xHalfWay = ( xPos0 + xPos1 ) * 0.5f;

						Asura_Vector_3 xFwd = xNormal ^ xPosDir;
						Asura_Render::RenderLine(xHalfWay,xHalfWay + xFwd,0xFF0000FF);
					}
				}
			}
		}
	}
}

void AVP_Attractors_System::UpdateAttractorTypes()
{
    static const u_int s_auRandomColours[] = 
    {
        0xFFFF2222,
        0xFF22FF22,
        0xFF2222FF,
        0xFFFFFF22,
        0xFFFF22FF,
        0xFF22FFFF,
        0xFFFFFFFF,
        0xFFAAAAAA
    };
    static const int s_iNumRandomColours = sizeof( s_auRandomColours ) / sizeof( s_auRandomColours[ 0 ] );

    // Determine our attractor types and names from whatever blueprints are in the "Env Hooks" blueprint type.
    Tools_Attractor_System::GetInstance()->UnregisterAllTypes();

	int iBPIndex = 0;
	for ( Asura_Blueprint_ConstIterator xBPIter( uAVP_BLUEPRINT_TYPE_ENV_HOOKS ); !xBPIter.Done(); xBPIter.Next() )
	{
		const Asura_Blueprint* pxBP = xBPIter.GetCurrent();
		Tools_Attractor_System::GetInstance()->RegisterType( pxBP->GetName().GetString(), s_auRandomColours[ iBPIndex++ % s_iNumRandomColours ] );
	}
}
#endif //#ifdef ASURA_TOOLS
