
//**************************************************************************/
// Copyright (c) 2008 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: August 2009
//**************************************************************************/

namespace mudbox {

class MBDLL_DECL NURBSCurve : public GroupNode
{
public:
	DECLARE_CLASS;

	NURBSCurve( int iDegree = 2, bool bFalloff = false );
	~NURBSCurve( void );

	inline bool operator ==( const NURBSCurve & ) const throw() { return false; };
	inline bool operator !=( const NURBSCurve &v ) const throw() { return !operator ==( v ); };

	void SetVisible( bool bVisible ) { m_bVisible = bVisible; };
	bool Visible( void ) { return m_bVisible; };
	bool AddCP( const Vector &v, int iIndex = -1 );
	void RemoveCP( unsigned int iIndex );
	void MoveCP( unsigned int iIndex, const Vector &v );
	void RecalcKnots( void );
	int GetDegree( void ) { return m_iDegree; };
	void SetDegree( int iDegree );
	Matrix GetMatrix( void );
	void Display( bool bScreen = true );
	void ProjectPoint( const Vector &vOrig, float fGuess, float &u, Vector &vProjected, float fMin = 0.001f, float fMax = 0.001f, int iMaxTry = 100 ) const;
	void BasisFunction( float u, int i, Store<float> &N ) const;
	void DeriveBasisFunction( int n, float u, int span, Store< Store<float> > &aDerF ) const;
	int FindSpan( float u ) const;
	void DeriveAt( float u, int iDeg, Store<Vector> &aDerivs ) const;
	inline float GetMaxParam( void ) const
	{
		if( m_aKnots.ItemCount() == 0 )
			return 0.0f;
		return m_aKnots[m_aKnots.ItemCount()-1];
	};
	inline Vector operator()( float u ) const
	{
		Vector v;
		if( m_iCPs <= m_iDegree )
			return v;

		Store<float> aN( "NURBSCurve temp" );
		int iSpan = FindSpan(u);
		BasisFunction( u, iSpan, aN );

		for( int i = m_iDegree; i >= 0; --i )
		{
			v += aN[i] * m_aCPs[iSpan-m_iDegree+i];
		};
		return v;
	};
	void Serialize( Stream &s );
	inline Vector TransformToWorld( const Vector &v )
	{
		Matrix m = GetMatrix();
		return m.Transform( v );
	};
	inline Vector TransformToLocal( const Vector &v )
	{
		Matrix m = GetMatrix();
		m.Invert();
		return m.Transform( v );
	};
	inline NURBSCurve &operator =( const NURBSCurve &c )
	{
		m_iDegree = c.m_iDegree;
		m_iCPs = c.m_iCPs;
		m_iSelectedCP = c.m_iSelectedCP;
		m_bEdited = c.m_bEdited;
		m_bVisible = c.m_bVisible;
		m_bClosed = c.m_bClosed;
		m_bPrecalculated = c.m_bPrecalculated;
		m_fTotalLength = c.m_fTotalLength;
		m_fPosX = c.m_fPosX;
		m_fPosY = c.m_fPosY;
		m_fAngle = c.m_fAngle;
		m_fScale = c.m_fScale;
		m_sName = c.m_sName;
		if( !m_pFalloff && c.m_pFalloff )
			m_pFalloff = new NURBSCurve( 3, false );
		if( c.m_pFalloff )
			*m_pFalloff = *(c.m_pFalloff);
		c.m_aCPs.Clone( m_aCPs );
		c.m_aKnots.Clone( m_aKnots );
		c.m_aPrecalc.Clone( m_aPrecalc );
		return *this;
	};
	void PrecalculateValues( void );
	void CalculateChordLengths( void );
	float GetChordPos( float fLength );
	void Close( void );

	int m_iCPs, m_iSelectedCP;
	bool m_bEdited, m_bVisible, m_bClosed, m_bPrecalculated;
	float m_fTotalLength;
	float m_fPosX, m_fPosY, m_fAngle, m_fScale;
	QString m_sName;
	Store<Vector> m_aCPs;
	Store<float> m_aKnots;
	Store<float> m_aPrecalc;
	NURBSCurve *m_pFalloff;

protected:
	int m_iDegree;

	struct ChordLength
	{
		ChordLength( float fPos = 0.0f, float fLength = 0.0f ) {m_fPos = fPos; m_fLength = fLength;};
		float m_fPos, m_fLength;
	};
	void CalculateChordLength( float fStart, float fEnd );
	Store<ChordLength> m_aChordLengths;
};

class MBDLL_DECL NURBSCurveEditor
{
public:
	NURBSCurveEditor( NURBSCurve *pCurve );
	~NURBSCurveEditor( void );
	void Refresh( void );
	void OnMouseClick( float fX, float fY );
	void OnMouseRelease( float fX, float fY );
	void OnMouseMove( float fX, float fY );
	void OnDelete( void );
	void MoveCPTo( const Vector &v );

	NURBSCurve *m_pCurve;
	Vector m_vDragDistance, m_vStartDrag;
};


MBDLL_DECL AttributeWidget *CreateNewCurveWidget( QWidget *pParent, int iWidth, Attribute *pAttribute );

class AttributeCurvePointer : public AttributePointer<NURBSCurve>
{
	AttributeWidget *CreateEditorWidget( QWidget *pParent, int iWidth ) { return CreateNewCurveWidget( pParent, iWidth, this ); };

public:
	AttributeCurvePointer( Node *pOwner = 0, const QString &sID = "") : AttributePointer<NURBSCurve>( pOwner, sID ) { };

	void SetPointerValue( NURBSCurve *cValue, bool bX = false )
	{
		NURBSCurve *curve = AttributePointer<NURBSCurve>::Value();
		NURBSCurve *newCurve = new NURBSCurve( 3 );
		if( curve  )
			delete curve;
		if( cValue )
			*newCurve = *cValue;
		AttributePointer<NURBSCurve>::SetPointerValue( newCurve, bX );
	};
};

typedef AttributeCurvePointer acurveptr;

}; // end of namespace mudbox
