#include "Weapon.h"
#include "Object.h"
#include "ModelStore.h"
#include "GameScreen.h"

#include <Utility/Log.h>
#include <Math/Vector.h>
#include <Renderer/Camera.h>
#include <Renderer/VisibleObject.h>
#include <Renderer/Color.h>
#include <Physics/PhysicalObject.h>

//////////////////////////////////////////////////////////////
Weapon::Weapon()
	: m_Speed( 40, 40, 40 )
	, m_Mass( 20, 20, 20 )
	, m_ProjectileCount( 1, 1, 1)
{
}

//////////////////////////////////////////////////////////////
Weapon::~Weapon()
{
}

namespace
{

std::vector<Math::Vector> GetOrigins( int Count )
{
	std::vector<Math::Vector> RetVal;
	switch (Count) {
	case 1:
		RetVal.push_back( Math::Vector() );
		break;
	case 2:
		RetVal.push_back( Math::Vector( 0.13, 0, 0 ) );
		RetVal.push_back( Math::Vector( -0.13, 0, 0 ) );
		break;
	case 3:
		RetVal.push_back( Math::Vector( 0.0, 0.15, 0 ) );
		RetVal.push_back( Math::Vector( 0.13, 0, 0 ) );
		RetVal.push_back( Math::Vector( -0.13, 0, 0 ) );
		break;
	case 4:
		RetVal.push_back( Math::Vector( 0.13, 0.13, 0 ) );
		RetVal.push_back( Math::Vector( -0.13, 0.13, 0 ) );
		RetVal.push_back( Math::Vector( 0.13, -0.13, 0 ) );
		RetVal.push_back( Math::Vector( -0.13, -0.13, 0 ) );
		break;
	}

	return RetVal;
}

}

//////////////////////////////////////////////////////////////
void Weapon::Fire( GameScreen& Game, AAssetManager& Manager, const Math::Point& Point )
{
	LOG_INFO( "FIRE!!!!" );
	Math::Vector X, Y, Z;
	Game.Camera()->CoordinateSpace().Axes( X, Y, Z );
	const std::vector<Math::Vector>& Origins = GetOrigins( m_ProjectileCount.Current );

	for (int i=0; i<Origins.size(); ++i) {

		ObjectPtr pObject( new Object( Game.GetModelStore()->GetVisibleModel( Manager, "Ball.ac" ),
									   Game.GetModelStore()->GetShadowModel( Manager, "Ball.shadow.ac" ),
									   Game.GetModelStore()->GetPhysicsModel( Math::CubeVector( 0.25f, 0.25f, 0.25f ) ), m_Mass.Current, false ) );

		pObject->Visible()->BaseColor() = Renderer::Color( 1.0f, 0, 0, 1.0f );
		Math::Vector Offset = X*Origins[i].X() + Y*Origins[i].Y();
		Math::Vector Position = Z;
		Position.SetLength( Game.Camera()->ZAxisOffset() );
		Position = Position + Offset;
		// I minus Z because the near clipping plane for the camera is 1
		// unit in front.  If you don't do this then the ball is drawn
		// half clipped as it moves past the near plane.
		pObject->SetPosition( Position-Z );

		pObject->Visible()->ObjectRenderer() = Game.Renderer();
		pObject->Visible()->ShadowRenderer() = Game.ShadowRenderer();

		Math::Size HalfDimensions = Game.Dimensions()/2.0f;
		float xPercent = static_cast<float>(Point.X() - HalfDimensions.X())/static_cast<float>(HalfDimensions.X());
		float yPercent = static_cast<float>(Point.Y() - HalfDimensions.Y())/static_cast<float>(HalfDimensions.Y());
		// Original versions of this algorithm used glu unproject.  However in
		// this version the GL runs on a different thread, and I can not call
		// it here.  So I just put together something that gives good results.
		Math::Vector Unprojected = X*xPercent*1.5 + Y*-yPercent + Z*-1;
		Math::Vector Velocity = Unprojected + (Offset * 0.3);
		Velocity.SetLength( m_Speed.Current );
		pObject->Physics()->SetVelocity( Velocity );
		Game.AddObject( pObject );
	}
}

//////////////////////////////////////////////////////////////
namespace {
// Pos will be 1 when moved to the top and 0 when moved
// to the bottom.
void SetRangeBySlider( Weapon::Range& Range, float Pos )
{
	if (Range.Up == Range.Down) {
		return;
	}
	if (Range.Up > Range.Down) {
		Range.Current = (Range.Up-Range.Down) * (Pos) + Range.Down;
	}
	else {
		Range.Current = (Range.Down-Range.Up) * (1-Pos) + Range.Up;
	}
}

}

//////////////////////////////////////////////////////////////
void Weapon::SliderPosition( float Pos )
{
	LOG_WARN( "Slider Pos: " << Pos );
	SetRangeBySlider( m_Speed, Pos );
	SetRangeBySlider( m_Mass, Pos );
	SetRangeBySlider( m_ProjectileCount, Pos );
}


