//
// Asura_Bicubic_Patch.h
//
// Created by Kevin Lea - 12/06/2001 11:04:50
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_BICUBIC_PATCH_H__
#define __ASURA_BICUBIC_PATCH_H__


#include "Core/Asura_Maths.h"
#include "Rendering/Asura_Render.h"
#include "Mesh/Asura_Mesh.h"
#include "Rendering/Asura_Surface.h"

#include "Core/Asura_PlatformBasics.h"


 
#define MAXIMUM_POSSIBLE_BICUBIC_ORDER 3
#define ASURA_BICUBIC_PATCH_MAX_NO_OF_VERTICES (((2<<(MAXIMUM_POSSIBLE_BICUBIC_ORDER-1))+1)*((2<<(MAXIMUM_POSSIBLE_BICUBIC_ORDER-1))+1))

struct Asura_Bicubic_Patch_Vertex
{
	Asura_Vector_3 du0dv0;	
	Asura_Vector_3 Normal;

		
	Asura_Vector_3 du2dv0;		
	Asura_Vector_3 du0dv2;		

	float u;
	float v;

	float u2;
	float v2;

	union
	{
		struct Asura_Colour_Components Colour;
		unsigned int Diffuse;
	};

	#if defined(ASURA_TARGET_SM3)
	Asura_SphericalHarmonics_Order3 m_xLightTransportHarmonic;
	Asura_SphericalHarmonics_Order2_RGB m_xDiffuseSH;

	Asura_Vector_3	m_xBrightestLightVector;	// VGP 13Feb2006

	Asura_Vector_3 m_xTangent;
	Asura_Vector_3 m_xBinormal;
	Asura_Vector_3 m_xTangent2;
	Asura_Vector_3 m_xBinormal2;
	#endif
	

	bool WantsSub;	
};

class Asura_Render_Patch_Vertex
{
	public:
		float X;
		float Y;
		float Z;

		Asura_Vector_3 Normal;


		union
		{
			unsigned int Colour;
			struct Colour_Components ColourComponents;
		};


		float U0;
		float V0;
			
		float U1;
		float V1;

};

class Asura_Bicubic_Patch
{
	public:
		Asura_Vector_3 ControlPoint[4][4];
		int m_iMaterialID;
	
		bool IsEndCap;
		bool IsCurved;

		inline void SetMaxDetailLevel(unsigned int level)
		{
			if (level>MAXIMUM_POSSIBLE_BICUBIC_ORDER)
				level=MAXIMUM_POSSIBLE_BICUBIC_ORDER;

			MaxDetailLevel = level;
		}

		float Bernstein(int i, float u);
		float Bernstein_FirstDif(int i, float u);
		float Bernstein_SecondDif(int i, float u);
		Asura_Vector_3 EvaluateCurve(float u, float v);
		Asura_Vector_3 EvaluateCurve_d2u(float u, float v);
		Asura_Vector_3 EvaluateCurve_d2v(float u, float v);
		Asura_Vector_3 EvaluateCurve_Normal(float u, float v);
	


		//bool BackFaceCulled(void);


		void Render(Asura_Surface* s, bool outputPolys=true);
		//void SendToVB(int vbIndex);
		void Asura_Bicubic_Patch::GetCorner(int index, Asura_Surface* s, int n);

	//private:
		inline void CalcUVs(Asura_Bicubic_Patch_Vertex* r, Asura_Bicubic_Patch_Vertex* v0, Asura_Bicubic_Patch_Vertex* v1, Asura_Bicubic_Patch_Vertex* v2);

		void EvaluateCorner(int index, float u, float v, int n);
		inline void CentralDifference(Asura_Vector_3& result, Asura_Vector_3& result_d2, float delta, Asura_Vector_3& curve_u1, Asura_Vector_3& curve_u2, Asura_Vector_3& curve_d2_u1, Asura_Vector_3& curve_d2_u2);
		int MaxDetailLevel;

		inline bool NewVertex(int index)
		{
			if (vIndexFrameNumber[index]!=ThisFrame) 
			{
				vIndexFrameNumber[index]=ThisFrame;
				vIndex[index] = nextAvailableVertex++;
				return true;
			}
			else
			{
				return false;
			}
//			if (nextAvailableVertex>=ASURA_BICUBIC_PATCH_MAX_NO_OF_VERTICES) exit(0);
		}

		inline Asura_Bicubic_Patch_Vertex* GetVertex(int index)
		{
			if (index>=ASURA_BICUBIC_PATCH_MAX_NO_OF_VERTICES) exit(0);
			return &Vertices[vIndex[index]];
		}

		inline void DestroyVertex(int index)
		{
			if (vIndexFrameNumber[index]==ThisFrame)
			{
				vIndexFrameNumber[index]=ThisFrame-1;
			//	vIndex[index] = -1;
				nextAvailableVertex--;
			}
		}

		inline bool ShouldSubdivide( const Asura_Vector_3& v1, const Asura_Vector_3& v2, const Asura_Vector_3& v3, const Asura_Vector_3& v4);
		inline bool ShouldSubdivideFlat( const Asura_Bicubic_Patch_Vertex* pxV1, const Asura_Bicubic_Patch_Vertex* pxV2, const Asura_Bicubic_Patch_Vertex* pxMidpoint );
		inline bool ShouldSubdivide_Length(Asura_Vector_3& v1, Asura_Vector_3& v2, Asura_Vector_3& v4);
		


		void AddTriangles(int c00, int c01, int c10, int c11);
		void AddTriangle(int c0, int c1, int c2);

		void SubdividePatch(int c00, int c01, int c10, int c11, int orderU, int orderV);
		
		void Asura_Bicubic_Patch::SubdivideFlatPatch(int c00, int c01, int c10, int c11, int orderU, int orderV);
		void Asura_Bicubic_Patch::SubdivideFlatTriPatch(int c00, int c01, int c10, int c11, int orderU, int orderV);

		void Asura_Bicubic_Patch::SubdividePatchTri(int c00, int c01, int c10, int c11, int orderU, int orderV);
	
		//void TestRender(int c00, int c01, int c10, int c11);


		void OutputTriangles(void);
		void OutputTriangles_GraphicNovel(void);
		//void OutputTrianglesTnL(void);
		//void OutputTrianglesToVB(int vbIndex);

		inline void CD_CurveAndFlat(Asura_Vector_3& result_curve, Asura_Vector_3& result_flat, Asura_Vector_3& result_d2, float delta, Asura_Vector_3& curve_u1, Asura_Vector_3& curve_u2, Asura_Vector_3& curve_d2_u1, Asura_Vector_3& curve_d2_u2);


		Asura_Bicubic_Patch_Vertex Vertices[ASURA_BICUBIC_PATCH_MAX_NO_OF_VERTICES];


		unsigned short indexOrder[1536];
	
		int vIndex[ASURA_BICUBIC_PATCH_MAX_NO_OF_VERTICES];
		int vIndexFrameNumber[ASURA_BICUBIC_PATCH_MAX_NO_OF_VERTICES];
		int nextAvailableVertex;
		int noOfEdgeVertices;

		int maxOrder; //not required(?)

		int noOfIndices;
		int ThisFrame;

		float subdivideSize;

};

#endif//__ASURA_BICUBIC_PATCH_H__
