///////////////////////////////////////////////////////////////////////////////
/// @file deSplinePatch.cpp
///
/// @brief Creation of splines
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Jul 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deLine.hpp"
#include "deArray.hpp"

class deSplinePatch : public IdeSplinePatch
{
public:
	deSplinePatch();
	~deSplinePatch();

	long Release();
	
	deBoolean SetNumControls(long Num);
	deBoolean SetNumControlSamples(long Num);
	deBoolean SetNumInterSamples(long Num);
	long GetNumControls() const;
	long GetNumControlSamples() const;
	long GetNumInterSamples() const;
	
	deBoolean SetControl(long index, IdeLine * control);
	deBoolean SetAllControls(long Num, IdeLine * controls[]);
	
	long GetNumVertices();
	long GetNumIndices();
	deVec3d * GetVertices();
	unsigned short * GetIndices();
	
private:
	deTArray <IdeLine*> m_ControlLines;
	long m_NumControlSamples;
	long m_NumInterSamples;
	deTArray <deVec3d> m_Vertices;
	deTArray <unsigned short> m_Indices;
	deBoolean m_ValidVertices;
	
	void CalculateVertices();
};

IdeSplinePatch* IdeLine_CreateSplinePatch()
{
	return new deSplinePatch;
}

deSplinePatch::deSplinePatch()
: m_ControlLines(0), m_ValidVertices(deFALSE), m_NumControlSamples(2), m_NumInterSamples(2)
{
}

deSplinePatch::~deSplinePatch()
{
}

long deSplinePatch::Release()
{
	delete this;
	return 0;
}

deBoolean deSplinePatch::SetNumControls(long Num)
{
	int i, len;
	IdeLine * line;
	if (Num < 0)
		return deFALSE;
	len = m_ControlLines.Length();
	for (i = Num; i < len; ++i)
	{
		line = m_ControlLines[i];
		if (line)
			line->Release();
	}
	m_ControlLines.Resize(Num);
	m_ValidVertices = deFALSE;
	return deTRUE;
}

deBoolean deSplinePatch::SetNumControlSamples(long Num)
{
	if (Num < 2)
		return deFALSE;
	m_NumControlSamples = Num;
	m_ValidVertices = deFALSE;
	return deFALSE;
}

deBoolean deSplinePatch::SetNumInterSamples(long Num)
{
	if (Num < 2)
		return deFALSE;
	m_NumInterSamples = Num;
	m_ValidVertices = deFALSE;
	return deFALSE;
}

long deSplinePatch::GetNumControls() const
{
	return m_ControlLines.Length();
}

long deSplinePatch::GetNumControlSamples() const
{
	return m_NumControlSamples;
}

long deSplinePatch::GetNumInterSamples() const
{
	return m_NumInterSamples;
}

deBoolean deSplinePatch::SetControl(long index, IdeLine * control)
{
	if (index < 0 || index >= (long)m_ControlLines.Length())
		return deFALSE;
	m_ControlLines[index] = control->Copy();
	m_ValidVertices = deFALSE;
	return deTRUE;
}

deBoolean deSplinePatch::SetAllControls(long Num, IdeLine * controls[])
{
	int i, len;
	IdeLine * line;
	if (Num < 0)
		return deFALSE;
	len = m_ControlLines.Length();
	for (i = 0; i < len; ++i)
	{
		line = m_ControlLines[i];
		if (line)
			line->Release();
	}
	m_ControlLines.Resize(Num);
	for (i = 0; i < Num; ++i)
	{
		m_ControlLines[i] = controls[i]->Copy();
	}
	m_ValidVertices = deFALSE;
	return deTRUE;
}

long deSplinePatch::GetNumVertices()
{
	return m_NumControlSamples * m_NumInterSamples;
}

long deSplinePatch::GetNumIndices()
{
	// just a triangle list for now
	return (m_NumControlSamples-1) * (m_NumInterSamples-1) * 6;
}

deVec3d * deSplinePatch::GetVertices()
{
	if (!m_ValidVertices)
		CalculateVertices();
	return m_Vertices.GetCArray();
}

unsigned short * deSplinePatch::GetIndices()
{
	if (!m_ValidVertices)
		CalculateVertices();
	return m_Indices.GetCArray();
}

void deSplinePatch::CalculateVertices()
{
	int i, j, len;
	IdeLine * line;
	deVec3d * pVerts, Vert;
	deDouble fraction, increment, linelen;

	len = m_ControlLines.Length();
	deTArray< deTArray<deVec3d> > ControlSamples(len);
	m_Vertices.Resize(m_NumControlSamples * m_NumInterSamples);

	for (i = 0; i < len; ++i)
	{
		line = m_ControlLines[i];
		linelen = line->GetLength();
		increment = linelen / (m_NumControlSamples-1);
		ControlSamples[i].Resize(m_NumControlSamples);
		fraction = 0.0;
		for (j = 0; j < m_NumControlSamples; ++j)
		{
			if (j+1 == m_NumControlSamples)
				fraction = linelen;
			line->GetPointOnLine(fraction, ControlSamples[i][j]);
			fraction += increment;
		}
	}

	increment = 1.0 / (m_NumInterSamples-1);
	for (i = 0; i < m_NumControlSamples; ++i)
	{
		line = IdeLine_CreateBezierLine();
		pVerts = new deVec3d[len];
		for (j = 0; j < len; ++j)
		{
			pVerts[j] = ControlSamples[j][i];
		}
		line->SetCVList(len, pVerts);
		delete[] pVerts;
		fraction = 0.0;
		for (j = 0; j < m_NumInterSamples; ++j)
		{
			if (j+1 == m_NumControlSamples)
				fraction = 1.0;
			line->GetPointOnLine(fraction, Vert);
			m_Vertices[i + j*m_NumInterSamples] = Vert;
			fraction += increment;
		}
		line->Release();
	}

	len = GetNumIndices();
	m_Indices.Resize(len);
	len /= 6;
	unsigned short * indices = m_Indices.GetCArray();
	int offset = -1;
	for (i = 0; i < len; ++i)
	{
		if ((i) % (m_NumInterSamples-1) == 0)
			++offset;
		indices[i*6+0] = (unsigned short)(offset + i);
		indices[i*6+1] = (unsigned short)(offset + i + 1);
		indices[i*6+2] = (unsigned short)(offset + i + m_NumInterSamples);

		indices[i*6+3] = (unsigned short)(indices[i*6+2]);
		indices[i*6+4] = (unsigned short)(indices[i*6+1]);
		indices[i*6+5] = (unsigned short)(indices[i*6+3] + 1);
	}

	m_ValidVertices = deTRUE;
}


/*
Method of making a spline patch

o   o   o   <<== control points

o   o   o

o   o   o


o---o---o   <<== control spline (quadratic)

o---o---o

o---o---o


*-*-*-*-*   <<== samples on control spline

*-*-*-*-*

*-*-*-*-*


* * * * *   <<== inter-splines (quadratic)
| | | | |
* * * * *
| | | | |
* * * * *


@ @ @ @ @   <<== samples on inter splines
@ @ @ @ @         vertices for patch tessellation
@ @ @ @ @
@ @ @ @ @
@ @ @ @ @

0 5
1 6
2 7
3 8
4   and so on

o is a control point defined in the map editor
* is a sample point on the control spline
@ is a sample point on a derived spline
- & | are splines derived from control points
can set any number of sample points in each direction
*/

