#include "SM_Engine3DPCH.h"
#include "SM_KeyFrameSequence.h"
#include "SM_D3DMesh.h"
#include "SM_DistortBall.h"
#include "SM_Shader.h"
#include "SM_Timer.h"
#include "SM_FVF.h"

using namespace ShaderManager;

const float c_pi = 3.1415926535897932384626433f;
const float c_2pi = 3.1415926535897932384626433f*2.0f;

typedef Vector3D vec3_t;
#define LENGTHOF(x) (sizeof(x)/sizeof(x[0]))

namespace
{

template<typename T>
T spline(float a, const T* ctrl, int numctrl)
{
	ASSERT(a>=0);
	ASSERT(a<=1);

	int numKnots = numctrl - 3;

	// figure out which knot we're on
	float t= a * (float)numKnots;
	int knot = floorf(t);

	// find u
	float u = t - (float)knot;
	float u2 = u*u;
	float u3 = u2*u;
	float ui = 1.0f - u;

	// find basis vals
	float b[4];
	b[0] = ui*ui*ui;
	b[1] = 3.0f*u3 - 6.0f*u2 + 4.0f;
	b[2] = -3.0f*u3 + 3.0f*u2 + 3.0f*u + 1.0f;
	b[3] = u3;
	
	T v = b[0]*ctrl[knot];
	v += b[1]*ctrl[knot+1];
	v += b[2]*ctrl[knot+2];
	v += b[3]*ctrl[knot+3];
	
	v *= 1.0f/6.0f;

	return v;
}

}

int SM_DistortBall::Init(int iShader, IKFAnimable* pAnimable)
{
	m_shader = iShader;
	m_pAnimable = pAnimable;

	// init mesh
	m_mesh.Init(sizeof(ntxvertex), ntxvertex_fvf, false);
	m_mesh.PrimType() = D3DPT_TRIANGLESTRIP;
	m_mesh.TriStripIndices(m_thetaSteps, m_phiSteps);

	return 0;
}


void SM_DistortBall::Render(RenderContext* prc, int iOutcode, float time)
{
	Update(time);

	float a = time*7.5f;
	float tr = 2.0f+4.0f;//*sin(a/3.333);
	
	//
	// update splines
	//
	m_s1[0] = .5*(1.5 + sin(0   *tr*c_pi + a));
	m_s1[1] = .5*(1.5 + sin(.2  *tr*c_pi + a));
	m_s1[2] = .5*(1.5 + sin(.4  *tr*c_pi + a));
	m_s1[3] = .5*(1.5 + sin(.6  *tr*c_pi + a));
	m_s1[4] = .5*(1.5 + sin(.8  *tr*c_pi + a));
	m_s1[5] = .5*(1.5 + sin(1   *tr*c_pi + a));
	
	m_s2[0] = 1+sin(0  *4.0f*c_pi + .3*a);
	m_s2[1] = 1+sin(.2 *4.0f*c_pi + .5*a);
	m_s2[2] = 1+sin(.4 *4.0f*c_pi + 1.1*a);
	m_s2[3] = 1+sin(.6 *4.0f*c_pi + 2.1*a);
	m_s2[4] = 1+sin(.8 *4.0f*c_pi + 1.1*a);
	m_s2[5] = 1+sin(1  *4.0f*c_pi + .5*a);


	//
	// get position and orientation for ball
	//
	Vector3D Pos;
	Quaternion q;
	if (m_pAnimable->m_pKeyFrameSequence)
	{
		m_pAnimable->m_pKeyFrameSequence->GetKeyFrame(time, 
		                                          m_pAnimable->m_fKeyFrameSequenceStartTime,
		                                          &Pos,
		                                          &q);
	}
	else
	{
		Pos = m_v3dPosition;
		q = m_qRotation;
	}

  
	Matrix4X4 mat;
	ToTransform(&mat, &Pos, &q);
	SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*)&mat);
	

	//
	// render
	//

	// ack shaders not working with exporter at all for me :(

	Shader* ps = GetShader(m_shader);
	if (ps)
	{
		for (int i=0; i<ps->m_uPasses; i++)
		{
			ps->SetShaderState(i);
			m_mesh.Render();
		}
	}
	else
	{
		m_mesh.Render();
	}
}

void SM_DistortBall::Update(float time)
{
	m_mesh.LockVerts();
	for (int iphi=0; iphi<m_phiSteps; iphi++)
	{
		float v = (float)iphi / (float)(m_phiSteps-1);
		const float phi = -c_pi*.5f + v*c_pi;
		for (int itheta=0; itheta<m_thetaSteps; itheta++)
		{
			const float u = (float)itheta / (float)(m_thetaSteps-1);
			const float theta = c_2pi * u;

			const float fx = cos(phi)*sin(theta);
			const float fy = sin(phi);
			const float fz = cos(phi)*cos(theta);

			float s = spline((fz+1.0f)/2.0f, m_s1, LENGTHOF(m_s1));
			float t = spline((fx+1.0f)/2.0f, m_s2, LENGTHOF(m_s2));
			const float r = m_radius * s*t;

			m_mesh.AddVert(&ntxvertex(vec3_t(r*fx,r*fy,r*fz)*0.35f, vec3_t(0,0,0), u,v));
		}
	}
	m_mesh.GenNormals(m_thetaSteps, m_phiSteps, true);
	m_mesh.UnlockVerts();
}

