/*******************************************************************************
 *	ATI 3D RAGE SDK sample code												   *	
 *																			   *
 *  Knight Demo																   *
 *																			   *
 *  Copyright (c) 1996-1997 ATI Technologies, Inc.  All rights reserved.	   *	
 *																			   *
 * Written by Aaron Orenstein												   *
 *  																		   *
 *	Earth sphincter death sequence. 										   *
 *******************************************************************************/
#include "stdwin.h"
#include "Util.h"
#include "Watchers.h"
#include "DirectDraw.h"
#include "multimedia.h"

#include "AtiDemoWnd.h"
#include "physics.h"
#include "a3d.h"
#include "ajm.h"

#include "normalmode.h"
#include "morph.h"
#include "sphincter.h"
#include "pedestal.h"

#include "filenames.h"

// -----------------------------------------------------------------------------

#define SPHY_PATH BASE_PATH "Sphy\\"

#define SPHINCTER00_FILENAME	SPHY_PATH "Sphy00.a3d"
#define SPHINCTER01_FILENAME	SPHY_PATH "Sphy01.a3d"
#define SPHINCTER02_FILENAME	SPHY_PATH "Sphy02.a3d"
#define SPHINCTER03_FILENAME	SPHY_PATH "Sphy03.a3d"
#define SPHINCTER04_FILENAME	SPHY_PATH "Sphy04.a3d"
#define SPHINCTER05_FILENAME	SPHY_PATH "Sphy05.a3d"
#define SPHINCTER06_FILENAME	SPHY_PATH "Sphy06.a3d"
#define SPHINCTER07_FILENAME	SPHY_PATH "Sphy07.a3d"
#define SPHINCTER08_FILENAME	SPHY_PATH "Sphy08.a3d"
#define SPHINCTER09_FILENAME	SPHY_PATH "Sphy09.a3d"
#define SPHINCTER10_FILENAME	SPHY_PATH "Sphy10.a3d"
#define SPHINCTER11_FILENAME	SPHY_PATH "Sphy11.a3d"
#define SPHINCTER12_FILENAME	SPHY_PATH "Sphy12.a3d"

#define KNIGHT_DEATH_FILENAME	SPHY_PATH "KSphyDie.ajm"

Vector SPHINCTER_POSITION = Vector(0, -470, -2964);
Vector SPHINCTER_ROTATION = Vector(M_PI/2, 0, 0);

// -----------------------------------------------------------------------------

extern BOOL g_blockPaint;

// -----------------------------------------------------------------------------

static Vector s_cameraPosition;
static Matrix s_cameraRotation(IDENTITY);

static uint32	s_currentTick;
static uint32	s_knightTick;
static Vector	s_startingCameraPosition;

#define DESIRED_CAMERA_ANGLE 5

static Vector s_position;
static Matrix s_rotation = Matrix(IDENTITY);

static JointMotion*		s_pKnightMotion;

static int s_sphincterStart = 141;

static float Xcenter, Zcenter;

// -----------------------------------------------------------------------------

static Object*	s_pSphincter[13];
static int s_sphincterTime[14] = {0, 18, 26, 29, 33, 40, 46, 52, 57, 61, 64, 69, 75};

static int s_sphincterTick = 0;
static int s_sphincterTickCopy;

// -----------------------------------------------------------------------------

void Sphincter::Cleanup(void) throw(Exception)
{
	delete s_pKnightMotion;
	s_pKnightMotion = NULL;

	for(int i=12; i>=0; i--)
	{
		delete s_pSphincter[i];
		s_pSphincter[i] = NULL;
	}
}

void Sphincter::Initialize(void) throw(Exception)
{
	BOOL success = FALSE;

	A3DRead(&s_pSphincter[0], SPHINCTER00_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[0], success, s_pSphincter00);

	A3DRead(&s_pSphincter[1], SPHINCTER01_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[1], success, s_pSphincter01);

	A3DRead(&s_pSphincter[2], SPHINCTER02_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[2], success, s_pSphincter02);

	A3DRead(&s_pSphincter[3], SPHINCTER03_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[3], success, s_pSphincter03);

	A3DRead(&s_pSphincter[4], SPHINCTER04_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[4], success, s_pSphincter04);

	A3DRead(&s_pSphincter[5], SPHINCTER05_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[5], success, s_pSphincter05);

	A3DRead(&s_pSphincter[6], SPHINCTER06_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[6], success, s_pSphincter06);

	A3DRead(&s_pSphincter[7], SPHINCTER07_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[7], success, s_pSphincter07);

	A3DRead(&s_pSphincter[8], SPHINCTER08_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[8], success, s_pSphincter08);

	A3DRead(&s_pSphincter[9], SPHINCTER09_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[9], success, s_pSphincter09);

	A3DRead(&s_pSphincter[10], SPHINCTER10_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[10], success, s_pSphincter10);

	A3DRead(&s_pSphincter[11], SPHINCTER11_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[11], success, s_pSphincter11);

	A3DRead(&s_pSphincter[12], SPHINCTER12_FILENAME);
	DECLARE_POINTER_WATCHER(Object*, s_pSphincter[12], success, s_pSphincter12);

	s_pKnightMotion = JointMotion::Read(KNIGHT_DEATH_FILENAME, g_pKnight);
	DECLARE_POINTER_WATCHER(JointMotion*, s_pKnightMotion, success, s_pKnightMotion);

	float Xradius = 211.765;
	float Zradius = 211.765;
	Xcenter = 0;
	Zcenter = -2964.705;
	float Uradius = 0.5;
	float Vradius = 0.5;
	float Ucenter = 0.5;
	float Vcenter = 0.5;

	for(int i=0; i<=12; i++)
	{
		for(GroupIterator iGroup=s_pSphincter[i]->GroupHead().Iterator(); !iGroup.Done(); iGroup.Next())
			for(PolyIterator iPoly=iGroup.Current()->PolyHead().Iterator(); !iPoly.Done(); iPoly.Next())
				for(int v=0; v<iPoly.Current()->VertexCount(); v++)
				{
					Vertex* p = &iPoly.Current()->GetVertex(v);
					p->U() = p->X() * Uradius / Xradius + Ucenter;
					p->V() = p->Y() * Vradius / Zradius + Vcenter;
				}
	}

	success = TRUE;
}

// -----------------------------------------------------------------------------

void Sphincter::SetMode(void)
{
	TRACE("Sphincter::SetMode()\n");

	EnterCriticalSection(&g_physicsMutex);

	// NormalMode knows how to draw all elements of the entire scene
	NormalMode::SetMode();

	s_cameraPosition = NormalMode::m_cameraPosition;
	s_cameraRotation = NormalMode::m_cameraRotation;

	s_position = Pedestal::Position();
	s_rotation = Pedestal::Rotation();

	s_currentTick = 0;
	s_knightTick = 0;

	g_physics.SetPhysicsMode(Physics);
	g_pWindow->SetKeyPressHandler(NULL);

	LeaveCriticalSection(&g_physicsMutex);
}

// -----------------------------------------------------------------------------

extern void DrawMorph(Clipper& clipper, Object* pObjectA, Object* pObjectB, float level, VertexType vType);



static void SphincterDraw(Clipper& rClipper)
{
	rClipper.Stack()->TransformObject(Matrix(ROTATION, SPHINCTER_ROTATION), SPHINCTER_POSITION);

	rClipper.Context()->SetTexture(g_pLandscapeTexture);

	if(s_sphincterTickCopy >= 0)
	{

		Object* objectA;
		Object* objectB;
		float	level;

		for(int i=0; i<12; i++)
			if((s_sphincterTickCopy >= s_sphincterTime[i]) && (s_sphincterTickCopy < s_sphincterTime[i+1]))
			{
				objectA = s_pSphincter[i];
				objectB = s_pSphincter[i+1];
				level = (float)(s_sphincterTickCopy - s_sphincterTime[i]) / (float)(s_sphincterTime[i+1] - s_sphincterTime[i]);
				break;
			}

		if(i < 12)
			DrawMorph(rClipper, objectA, objectB, level, VERTEXTYPE_T);

		if((i > 0) && (i < 11))
		{
			Vertex v[15];

			{
				int count = 0;
				for(GroupIterator iGroup = objectA->GroupHead().Iterator(); (count < 15) && !iGroup.Done(); iGroup.Next())
					for(PolyIterator iPoly = iGroup.Current()->PolyHead().Iterator(); (count < 15) && !iPoly.Done(); iPoly.Next())
						for(int j=0; (count < 15) && (j < iPoly.Current()->VertexCount()); j++)
							v[count++].XYZ() = iPoly.Current()->GetVertex(j).XYZ();
			}

			{
				int count = 0;
				for(GroupIterator iGroup = objectB->GroupHead().Iterator(); (count < 15) && !iGroup.Done(); iGroup.Next())
					for(PolyIterator iPoly = iGroup.Current()->PolyHead().Iterator(); (count < 15) && !iPoly.Done(); iPoly.Next())
						for(int j=0; (count < 15) && (j < iPoly.Current()->VertexCount()); j++)
						{
							v[count].XYZ() = (iPoly.Current()->GetVertex(j).XYZ() - v[count].XYZ()) * level + v[count].XYZ();
							count++;
						}
			}

			Vertex center = Vertex(Vector(0, 0, v[0].Z()));
			for(int j=0; j<15; j++)
				rClipper.DrawTriangle(center, v[j], v[(j+1)%14], RGBA(0, 0, 0));
		}
	}

	rClipper.Stack()->TransformDone();
}

// -----------------------------------------------------------------------------

void Sphincter::Physics(PhysicsType type) throw()
{
	if(type == PHYSICS_COPY)
	{
		s_sphincterTickCopy = s_sphincterTick;
		if(s_knightTick < s_pKnightMotion->TickCount())
			s_pKnightMotion->SetTick(s_knightTick);
		else
			s_pKnightMotion->SetTick(s_pKnightMotion->TickCount()-1);
		NormalMode::m_knightPosition = s_position + s_rotation * g_pKnight->Translation();
		g_pKnight->Translation() = Vector(ZERO);
		NormalMode::m_knightRotation = s_rotation;

		if(g_pCameraScript)
			g_pCameraScript->Copy();

		NormalMode::Physics(PHYSICS_COPY);
	}
	else
	{
		if(g_pCameraScript)
			g_pCameraScript->Tick();

		NormalMode::m_pUserDraw = SphincterDraw;

		if(s_currentTick > 149)
			NormalMode::m_knightShadow = FALSE;

		s_sphincterTick = s_currentTick - s_sphincterStart;

		NormalMode::Physics(PHYSICS_RUN);

		s_currentTick++;
		s_knightTick++;
		if(s_knightTick == s_pKnightMotion->TickCount()+180)
		{
			Morph::SetMode();
		}
	}
}

// -----------------------------------------------------------------------------

static Vector MorphVector(const Vector& a, const Vector& b, float level)
{
	return (b - a) * level + a;
}

static Normal MorphNormal(const Normal& a, const Normal& b, float level)
{
	return (b - a) * level + a;
}

static Vertex MorphVertex(const Vertex& a, const Vertex& b, float level, VertexType vType)
{
	Vertex v;
	v.X() = (b.X() - a.X()) * level + a.X();
	v.Y() = (b.Y() - a.Y()) * level + a.Y();
	v.Z() = (b.Z() - a.Z()) * level + a.Z();

	if(IsTextured(vType))
	{
		v.U() = (b.U() - a.U()) * level + a.U();
		v.V() = (b.V() - a.V()) * level + a.V();
	}

	if(IsColored(vType))
	{
		v.R() = (b.R() - a.R()) * level + a.R();
		v.G() = (b.G() - a.G()) * level + a.G();
		v.B() = (b.B() - a.B()) * level + a.B();
		v.A() = (b.A() - a.A()) * level + a.A();
	}

	return v;
}

static void DrawMorph(Clipper& clipper, Group* pGroupA, Group* pGroupB, float level, VertexType vType)
{
	Vector center = MorphVector(pGroupA->Center(), pGroupB->Center(), level);

	clipper.Stack()->TransformObject(Matrix(IDENTITY), center);
	clipper.Stack()->TransformObject(Matrix(ROTATION, MorphVector(pGroupA->Rotation(), pGroupB->Rotation(), level)),
											 MorphVector(pGroupA->Translation(), pGroupB->Translation(), level));
	clipper.Stack()->TransformObject(Matrix(IDENTITY), -center);

	if(pGroupA->Visible() && pGroupB->Visible() &&
	   !pGroupA->Hidden() && !pGroupB->Hidden())
	{
		PolyIterator iPolyA = pGroupA->PolyHead().Iterator();
		PolyIterator iPolyB = pGroupB->PolyHead().Iterator();
		while(!iPolyA.Done() && !iPolyB.Done())
		{
			int faces = min(iPolyA.Current()->FaceCount(), iPolyB.Current()->FaceCount());
			for(int i=0; i<faces; i++)
			{
				Normal n = MorphNormal(iPolyA.Current()->GetFaceNormal(i), iPolyB.Current()->GetFaceNormal(i), level);
				if(clipper.Stack()->IsFacing(n))
				{
					Vertex a = MorphVertex(iPolyA.Current()->GetFaceVertex(i, 0), iPolyB.Current()->GetFaceVertex(i, 0), level, vType);
					Vertex b = MorphVertex(iPolyA.Current()->GetFaceVertex(i, 1), iPolyB.Current()->GetFaceVertex(i, 1), level, vType);
					Vertex c = MorphVertex(iPolyA.Current()->GetFaceVertex(i, 2), iPolyB.Current()->GetFaceVertex(i, 2), level, vType);
					clipper.DrawTriangle(a, b, c, vType);
				}
				else
				{
					Vertex a = MorphVertex(iPolyA.Current()->GetFaceVertex(i, 0), iPolyB.Current()->GetFaceVertex(i, 0), level, vType);
					Vertex b = MorphVertex(iPolyA.Current()->GetFaceVertex(i, 1), iPolyB.Current()->GetFaceVertex(i, 1), level, vType);
					Vertex c = MorphVertex(iPolyA.Current()->GetFaceVertex(i, 2), iPolyB.Current()->GetFaceVertex(i, 2), level, vType);
					clipper.DrawTriangle(a, b, c, RGBA(0, 0, 0));
				}
			}

			iPolyA.Next();
			iPolyB.Next();
		}
	}

	if(pGroupA->Node().Child() && pGroupB->Node().Child())
	{
		GroupIterator iChildA = pGroupA->Node().Iterator(PARENT_FIRST);
		GroupIterator iChildB = pGroupB->Node().Iterator(PARENT_FIRST);
		while(!iChildA.Done() && !iChildB.Done())
		{
			DrawMorph(clipper, iChildA.Current(), iChildB.Current(), level, vType);

			iChildA.SkipChildren();
			iChildB.SkipChildren();
		}
	}

	clipper.Stack()->TransformDone();
	clipper.Stack()->TransformDone();
	clipper.Stack()->TransformDone();
}

void DrawMorph(Clipper& clipper, Object* pObjectA, Object* pObjectB, float level, VertexType vType)
{
	clipper.Stack()->TransformObject(Matrix(IDENTITY), MorphVector(pObjectA->TranslationCopy(), pObjectB->TranslationCopy(), level));

	GroupIterator iGroupA = pObjectA->GroupHead().Iterator(PARENT_FIRST);
	GroupIterator iGroupB = pObjectB->GroupHead().Iterator(PARENT_FIRST);
	while(!iGroupA.Done() && !iGroupB.Done())
	{
		DrawMorph(clipper, iGroupA.Current(), iGroupB.Current(), level, vType);

		iGroupA.SkipChildren();
		iGroupB.SkipChildren();
	}

	clipper.Stack()->TransformDone();
}


// -----------------------------------------------------------------------------
