#ifndef __TMCBezierPath__
#define __TMCBezierPath__
/****************************************************************************************


		TMCBezierPath.h

		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	Ray Dream (Emil)
		Date:	12/14/98

		Revision History

		when		who			what & why
		==========	==========	======================================================
		12/14/98	Joshua		Ported to KWAK

*****************************************************************************************/

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCArray.h"
#include "MCPtrArray.h"
#include "IShUtilities.h"
#include "IShTokenStream.h"
#include "RealQuat.h"
#include "Angle.h"


/// Lists the different kind of bezier points
///
enum EBezierType
{
	kContinuous0=0,		///< curve countinuous
	kContinuous1=1,		///< derivative continuous
	kEqualTangeant=2,	///< Left and right tan are equal
	kZero=3
};

#define kAlignPath		'algn'
#define kDontAlignPath	'none'

class TMCBezierPath;

/// A bezier point with its demi tangeants
///
class TMCLocationRec
{
public:
	TMCLocationRec();

	real		fSegmentBeginPos;

	TVector3	fLocation;
	real		fLength;
	real		fLengthFactor;
	EBezierType fType;

	TMCArray<TVector3>			fLinearApprox;
	TMCArray<real>				fLinearApproxDistance;
	TMCArray<real>				fLinearApproxBezierPos;

	/// These values aren't exactly the normal acceleration: a smoothing is done
	/// to avoid discontinuity at the junction of 2 beziers curves.
	///
	TMCArray<TVector3>			fNormalAcceleration;

	TVector3					fLastBankingV; ///< Not used ?

	TMCLocationRec*	Clone() const;

	// Setters
	inline void SetLocation(const TVector3& newLocation) { fLocation=newLocation; }
	void SetNextControlDistance(const real32 dist);
	void SetPrevControlDistance(const real32 dist);
	void SetNextControlDirection(const TVector3& dir);
	void SetPrevControlDirection(const TVector3& dir);
	void SetNextControlLocation(const TVector3& newNextControl);
	void SetPrevControlLocation(const TVector3& newPrevControl);

	// Getters
	inline TVector3&  GetLocation(){return fLocation;}
	inline TVector3& GetNextControlDirection(){ return fNextControlDirection; }
	inline TVector3& GetPrevControlDirection(){ return fPrevControlDirection; }
	inline real32 GetNextControlDistance(){ return fNextControlDistance; }
	inline real32 GetPrevControlDistance(){ return fPrevControlDistance; }
	TVector3 GetNextControlLocation();
	TVector3 GetPrevControlLocation();
	TVector3& GetNextVectorUp(){ return fNextVectorUp; }
	TVector3& GetPrevVectorUp(){ return fPrevVectorUp; }
	TVector3& GetNextControlVectorUp(){ return fNextControlVectorUp; }
	TVector3& GetPrevControlVectorUp(){ return fPrevControlVectorUp; }

	void CopyDataFrom(const TMCLocationRec * from);

	void CalcLinearApprox		(TMCLocationRec * nextRec, int16 level=0, real posMin = 0.0f, real posMax = 1.0f);
	void CalcApprox				(TMCLocationRec * nextRec, TMCLocationRec * prevRec, TMCBezierPath* bezierPath);
	void CalcLength				(TMCLocationRec * nextRec);
	void CalcNormalAcceleration (TMCLocationRec * nextRec, TMCLocationRec * prevRec);
	void CalcVectorUp			(TMCLocationRec * nextRec, TMCLocationRec * prevRec, const boolean isLinear);
	void CalcPseudoCurv			(TMCLocationRec * nextRec, TMCLocationRec * prevRec);

	real DistanceToBezierPos	(real linearDistance, TMCLocationRec * nextRec);

	void Interpolate			(real segmentBezierPos, TVector3& outPoint, TMCLocationRec * nextRec);
	/// Return 1/3th of the actual speed
	///
	void GetSpeed				(real segmentBezierPos, TVector3 & point, TMCLocationRec * nextRec);

	/// Return 1/6th of the actual acceleration
	///
	void GetAcceleration		(real segmentBezierPos, TVector3 & point, TMCLocationRec * nextRec);
	void GetNormalAcceleration	(real segmentBezierPos, TVector3 & point, TMCLocationRec * nextRec);
	void GetVectorUp			(real segmentBezierPos, TVector3 & speed, TVector3 & point, TMCLocationRec * nextRec);
	void Subdivide				(real segmentBezierPos, TMCLocationRec * nextRec, TMCLocationRec & rec1, TMCLocationRec & rec2, TMCLocationRec & rec3);

	// Tangent properties modification
	void ConvertTangent				();
	void ConvertTangentDerivative	();
	void Link						();
	void LinkDerivative				();
	void UnLink						();
	void Degenerate					();

protected:

	real CalcError(TMCLocationRec * nextRec);


	/// The VectorUp is normal to the path and is used to define a base attached to the path.
	/// Its determination is based on the fNextVectorUp of the previous TMCLocationRec as this:
	/// it's the projection of the previous fVectorUp on the plane normal to the tangeant.
	/// There're 2 vector per TMCLocationRec because it can have a discontinuity. fNextVectorUp
	/// is computed from fPrevVectorUp
	TVector3	fPrevControlVectorUp;
	TVector3	fPrevVectorUp;
	TVector3	fNextVectorUp;
	TVector3	fNextControlVectorUp;

	/// PrevControl and NextControl relative to Location: 
	/// a normalize vector and a distance => we are so able
	/// to keep a direction even if the distance is 0
	TVector3	fPrevControlDirection;
	TVector3	fNextControlDirection;
	real32		fPrevControlDistance;
	real32		fNextControlDistance;
};

/// A list of pointers to TMCLocationRec onjects.
///
class TMCLocationsList : public TMCPtrArray<TMCLocationRec>
{
public:
	TMCLocationsList();
	~TMCLocationsList();

	void DeleteAllLocations		();
	void CopyDataFrom			(const TMCLocationsList * fromList);
};

/// \addtogroup Data
/// \{

/// A 3D bezier curve
///
class TMCBezierPath
{
public:
	TMCBezierPath();
	~TMCBezierPath();



	real	GeTMCLocationRecs		(real pathPos, TMCLocationRec * &rec, TMCLocationRec * &nextRec, int32 * recIndex=nil);

	void	GetLocation			(real pathBezierPos, TVector3& outPoint);

	void	SetLocation			(const TMCLocationRec & newLoc);

	void	ReCalcAllApprox		();
	int16	GetNbApproxPoints	();
	void	ComputeSegmentBeginPos();

	void	ReCalcAllVectorUp	();
	void	ReCalcAllPseudoCurv	();

	//Init method
	void Init();

	// Copy method
	void CopyDataFrom(const TMCBezierPath * from);

	TMCLocationsList  fLocations;
	int32			fAlignAndBankType; // Not used ?
	real			fMaxBanking; // Not used ?

	// Data access
	void SetIsLinear(boolean isLinear){fIsLinear=isLinear;}
	boolean IsLinear(){return fIsLinear;}

protected:
	boolean fIsLinear; // true if linear line, false if Bezier interpolation
};

/// A 2D Bezier curve in XY ( Z is not used here ).
/// The Bezier segment don't overlap themselves in X ( could be written Y=f(X) ).
///
class TMCBezierCurve : public TMCBezierPath
{
public:
	/// Get Y value
	///
	real32	GetYPos				(real32 XPos);

	/// Add point. Return index of the new point
	///
	int32	SubdivideXYPath		(real32 XPos);

protected:
	real32	GetYRecsWithDichotomizedLambda(real32 pathPos, TMCLocationRec * &rec, TMCLocationRec * &nextRec, int32 * recIndex);
	real32	GetYRecs(real32 pathPos, TMCLocationRec * &rec, TMCLocationRec * &nextRec, int32 * recIndex);
};

/// \}

/// iterator over the linear approximation of a bezier path
///
class TMCPathPositionIterator
{
public:
	TMCPathPositionIterator	(TMCBezierPath* bezierPath);
	~TMCPathPositionIterator	();

	TVector3*	First		(real& pos);
	boolean		More		();
	TVector3*	Next		(real& pos);

protected:
	TMCBezierPath*	fBeizerPath;
	TMCLocationRec*	fCurRec;
	TMCPtrArray<TMCLocationRec>::iterator* fLocIter;
	TMCArray<TVector3>::iterator* fCurRecIter;
	real			fCurPos;
	TVector3*		fPrevPos;
	boolean			fMore;
	boolean			fGaveLastLocation;
};



/// Helper class to read or write a TMCLocationRec from/to an IShTokenStream
///
class TMCLocationRecReadWrite
{
public:
	TMCLocationRecReadWrite(TMCLocationRec * rec) {fRec=rec;}

	MCCOMErr Read(IShTokenStream* stream);
	MCCOMErr Write(IShTokenStream* stream);
protected:
	TMCLocationRec * fRec;
};

#endif


