/**
* Path Segment
* @version: 01-29-2005
* @author Joseph 'Pcjoe' Florencio
*/
#ifndef FXPATHSEGMENT_H
#define FXPATHSEGMENT_H

class CLinearPath;
class CBezierPath;

// Bezier curve calculations
#define	B1(t)	(t*t*t)
#define	B2(t)	(3*t*t*(1.0-t))
#define	B3(t)	(3*t*(1.0-t)*(1.0-t))
#define B4(t)	((1.0-t)*(1.0-t)*(1.0-t))
#define BezierCalc(a,b,c,d,t)	(a*B1(t)+b*B2(t)+c*B3(t)+d*B4(t))

// Bezier curve length detail
#define BEZIER_DETAIL		double(double(1)/double(32))	// Low detail seems to work fine ^_^
#define BEZIER_DETAIL_CHEAP double(double(1)/double(16))
#define BEZIER_USE_CHEAP	128
#define BEZIER_DIST_DETAIL	double(double(1)/double(64))

class CPathSegment
{
public:
	// Constructor
	CPathSegment();

	// Get Length
	virtual float getLength( void ) { return 0; }

	// Get Point
	virtual vec3_t getPoint( float flDist ) { return vec3_t(0,0,0); }

	// Get Start Point
	virtual vec3_t getStartPoint( void ) { return vec3_t(0,0,0); }
	
	// Get End Point
	virtual vec3_t getEndPoint( void ) { return vec3_t(0,0,0); }

	// Path type
	virtual bool linearPath( void ) { return false; }
	virtual bool bezierPath( void ) { return false; }

	// Move points by specified vector
	virtual void movePoints( vec3_t move ) { }

	// Rotate points by a specified angle
	virtual void rotatePoints( vec3_t angle ) { }

	// Render
	virtual void RenderSegment( bool bSelected ) { }

	// Write XML
	virtual TiXmlElement *writeXML( void ) { return NULL; }

	// read XML
	virtual void readXML( TiXmlElement *pElement ) { }

	// FX Event
	void setFXEvent( CFXEvent *pEvent ) { m_pEvent = pEvent; }
	CFXEvent *getFXEvent( void ) { return m_pEvent; }
protected:
	CFXEvent	*m_pEvent;
};

class CLinearPath : public CPathSegment
{
public:
	CLinearPath();
	bool linearPath( void ) { return true; }

	// Get Length
	float getLength( void );

	// Add Static Point
	void addPoint( vec3_t v );
	// Add Data Point
	void addPoint( CFXDataPoint dp );
	// Add Dynamic Bone Point
	void addPoint( std::string bone );
	// Add Camera Point
	void addCameraPoint( void );

	// Change this linear path to a bezier path
	CBezierPath *toBezierPath( void );
	
	// Move points by specified vector
	void movePoints( vec3_t move );

	// Rotate points by a specified angle
	void rotatePoints( vec3_t angle );

	// Get Point
	vec3_t getPoint( float flDist );

	// Get Start Point
	vec3_t getStartPoint( void );
	
	// Get End Point
	vec3_t getEndPoint( void );

	// Render
	void RenderSegment( bool bSelected );

	// Write XML
	virtual TiXmlElement *writeXML( void );

	// read XML
	virtual void readXML( TiXmlElement *pElement );

//private:
	std::list<CFXDataPoint>	m_Points;
};

class CBezierPoints
{
public:
	CBezierPoints();
	CBezierPoints( CFXDataPoint org, CFXDataPoint curveto );

	// Accessors
	// Used to get the inverse curve to
	void setFirst( bool first ) { m_bFirst = first; }
	bool isFirst( void ) { return m_bFirst; }
	CFXDataPoint *getOrigin( void ) { return &m_Org; }
	CFXDataPoint *getCurveTo( void ) { return &m_CurveTo; }
	vec3_t	getInverseCurveTo( void )
	{ return (getOrigin()->getPosition() - (getCurveTo()->getPosition()-getOrigin()->getPosition())); }

	void setOrigin( CFXDataPoint org ) { m_Org = org; }
	void setCurveTo( CFXDataPoint curveto ) { m_CurveTo = curveto; }

private:
	CFXDataPoint m_Org;
	CFXDataPoint m_CurveTo;
	bool m_bFirst;
};

class CBezierPath : public CPathSegment
{
public:
	CBezierPath();
	bool bezierPath( void ) { return true; }

	// Accessors
	float getLength( void ) { return m_flLength; }
	void setLength( float flLength ) { m_flLength = flLength; }

	// Get point on curve (Percent)
	vec3_t getPointOnCurveP( vec3_t a, vec3_t b, vec3_t c, vec3_t d, double s );

	// Get point on curve (distance)
	vec3_t getPointOnCurveD( CBezierPoints *bp1, CBezierPoints *bp2, double dist );

	// Get point on curve (Distance)
	vec3_t getPointOnCurveD( vec3_t a, vec3_t b, vec3_t c, vec3_t d, double dist );

	// Get length between bezier points
	float getLengthBezierPoints( vec3_t a, vec3_t b, vec3_t c, vec3_t d );
	float getLengthBezierPoints( CBezierPoints *bp1, CBezierPoints *bp2 );

	// Calculate Length
	void calculateLength( void );

	// Add points
	void addPoints( CFXDataPoint dp1, CFXDataPoint dp2 );

	// Change this linear path to a bezier path
	CLinearPath	*toLinearPath( void );
	
	// Move points by specified vector
	void movePoints( vec3_t move );

	// Rotate points by a specified angle
	void rotatePoints( vec3_t angle );

	// Get Point
	vec3_t getPoint( float flDist );

	// Get Start Point
	vec3_t getStartPoint( void );
	
	// Get End Point
	vec3_t getEndPoint( void );

	// Render
	void RenderSegment( bool bSelected );

	// Write XML
	virtual TiXmlElement *writeXML( void );

	// read XML
	virtual void readXML( TiXmlElement *pElement );
private:
	float						m_flLength;
public:
	std::list<CBezierPoints>	m_BezierPoints;
};

#endif