/*
   Copyright (C) 2001-2006, William Joseph.
   All Rights Reserved.

   This file is part of GtkRadiant.

   GtkRadiant is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   GtkRadiant is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GtkRadiant; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#if !defined( INCLUDED_PATCH_H )
#define INCLUDED_PATCH_H

/// \file
/// \brief The patch primitive.
///
/// A 2-dimensional matrix of vertices that define a quadratic bezier surface.
/// The Boundary-Representation of this primitive is a triangle mesh.
/// The surface is recursively tesselated until the angle between each triangle
/// edge is smaller than a specified tolerance.

#include "globaldefs.h"
#include "nameable.h"
#include "ifilter.h"
#include "imap.h"
#include "ipatch.h"
#include "cullable.h"
#include "renderable.h"
#include "editable.h"
#include "selectable.h"

#include "debugging/debugging.h"

#include <set>
#include <limits>

#include "math/frustum.h"
#include "string/string.h"
#include "stream/stringstream.h"
#include "stream/textstream.h"
#include "xml/xmlelement.h"
#include "scenelib.h"
#include "transformlib.h"
#include "instancelib.h"
#include "selectionlib.h"
#include "traverselib.h"
#include "render.h"
#include "stringio.h"
#include "shaderlib.h"
#include "generic/callback.h"
#include "signal/signalfwd.h"
#include "texturelib.h"
#include "xml/ixml.h"
#include "dragplanes.h"

enum EPatchType
{
	ePatchTypeQuake3,
	ePatchTypeDoom3,
};

extern int g_PatchSubdivideThreshold;


#define MIN_PATCH_WIDTH 3
#define MIN_PATCH_HEIGHT 3

extern std::size_t MAX_PATCH_WIDTH;
extern std::size_t MAX_PATCH_HEIGHT;

#define MAX_PATCH_ROWCTRL ( ( ( MAX_PATCH_WIDTH - 1 ) - 1 ) / 2 )
#define MAX_PATCH_COLCTRL ( ( ( MAX_PATCH_HEIGHT - 1 ) - 1 ) / 2 )

enum EPatchCap
{
	eCapBevel,
	eCapEndCap,
	eCapIBevel,
	eCapIEndCap,
	eCapCylinder,
};

enum EPatchPrefab
{
	ePlane,
	eBevel,
	eEndCap,
	eCylinder,
	eDenseCylinder,
	eVeryDenseCylinder,
	eSqCylinder,
	eCone,
	eSphere,
	eXactCylinder,
	eXactSphere,
	eXactCone,
};

enum EMatrixMajor
{
	ROW, COL,
};

struct BezierCurve
{
	Vector3 crd;
	Vector3 left;
	Vector3 right;
};

const std::size_t BEZIERCURVETREE_MAX_INDEX = std::size_t( 1 ) << ( std::numeric_limits<std::size_t>::digits - 1 );

struct BezierCurveTree
{
	std::size_t index;
	BezierCurveTree* left;
	BezierCurveTree* right;
};

inline bool BezierCurveTree_isLeaf( const BezierCurveTree* node ){
	return node->left == 0 && node->right == 0;
}

void BezierCurveTree_Delete( BezierCurveTree *pCurve );


inline VertexPointer vertexpointer_arbitrarymeshvertex( const ArbitraryMeshVertex* array ){
	return VertexPointer( VertexPointer::pointer( &array->vertex ), sizeof( ArbitraryMeshVertex ) );
}

typedef PatchControl* PatchControlIter;
typedef const PatchControl* PatchControlConstIter;

inline void copy_ctrl( PatchControlIter ctrl, PatchControlConstIter begin, PatchControlConstIter end ){
	std::copy( begin, end, ctrl );
}

const Colour4b colour_corner( 0, 255, 0, 255 );
const Colour4b colour_inside( 255, 0, 255, 255 );

class Patch;

class PatchFilter
{
public:
virtual bool filter( const Patch& patch ) const = 0;
};

bool patch_filtered( Patch& patch );
void add_patch_filter( PatchFilter& filter, int mask, bool invert = false );

void Patch_addTextureChangedCallback( const SignalHandler& handler );
void Patch_textureChanged();

inline void BezierCurveTreeArray_deleteAll( Array<BezierCurveTree*>& curveTrees ){
	for ( Array<BezierCurveTree*>::iterator i = curveTrees.begin(); i != curveTrees.end(); ++i )
	{
		BezierCurveTree_Delete( *i );
	}
}

inline void PatchControlArray_invert( Array<PatchControl>& ctrl, std::size_t width, std::size_t height ){
	Array<PatchControl> tmp( width );

	PatchControlIter from = ctrl.data() + ( width * ( height - 1 ) );
	PatchControlIter to = ctrl.data();
	for ( std::size_t h = 0; h != ( ( height - 1 ) >> 1 ); ++h, to += width, from -= width )
	{
		copy_ctrl( tmp.data(), to, to + width );
		copy_ctrl( to, from, from + width );
		copy_ctrl( from, tmp.data(), tmp.data() + width );
	}
}

class PatchTesselation
{
public:
PatchTesselation()
	: m_numStrips( 0 ), m_lenStrips( 0 ), m_nArrayWidth( 0 ), m_nArrayHeight( 0 ){
}
Array<ArbitraryMeshVertex> m_vertices;
Array<RenderIndex> m_indices;
std::size_t m_numStrips;
std::size_t m_lenStrips;

Array<std::size_t> m_arrayWidth;
std::size_t m_nArrayWidth;
Array<std::size_t> m_arrayHeight;
std::size_t m_nArrayHeight;

Array<BezierCurveTree*> m_curveTreeU;
Array<BezierCurveTree*> m_curveTreeV;
};

class RenderablePatchWireframe : public OpenGLRenderable
{
PatchTesselation& m_tess;
public:
RenderablePatchWireframe( PatchTesselation& tess ) : m_tess( tess ){
}
void render( RenderStateFlags state ) const {
	{
  #if NV_DRIVER_BUG
		glVertexPointer( 3, GL_FLOAT, 0, 0 );
		glDrawArrays( GL_TRIANGLE_FAN, 0, 0 );
  #endif

		std::size_t n = 0;
		glVertexPointer( 3, GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->vertex );
		for ( std::size_t i = 0; i <= m_tess.m_curveTreeV.size(); ++i )
		{
			glDrawArrays( GL_LINE_STRIP, GLint( n ), GLsizei( m_tess.m_nArrayWidth ) );

			if ( i == m_tess.m_curveTreeV.size() ) {
				break;
			}

			if ( !BezierCurveTree_isLeaf( m_tess.m_curveTreeV[i] ) ) {
				glDrawArrays( GL_LINE_STRIP, GLint( m_tess.m_curveTreeV[i]->index ), GLsizei( m_tess.m_nArrayWidth ) );
			}

			n += ( m_tess.m_arrayHeight[i] * m_tess.m_nArrayWidth );

		}
	}

	{
		const ArbitraryMeshVertex* p = m_tess.m_vertices.data();
		std::size_t n = m_tess.m_nArrayWidth * sizeof( ArbitraryMeshVertex );
		for ( std::size_t i = 0; i <= m_tess.m_curveTreeU.size(); ++i )
		{
			glVertexPointer( 3, GL_FLOAT, GLsizei( n ), &p->vertex );
			glDrawArrays( GL_LINE_STRIP, 0, GLsizei( m_tess.m_nArrayHeight ) );

			if ( i == m_tess.m_curveTreeU.size() ) {
				break;
			}

			if ( !BezierCurveTree_isLeaf( m_tess.m_curveTreeU[i] ) ) {
				glVertexPointer( 3, GL_FLOAT, GLsizei( n ), &( m_tess.m_vertices.data() + ( m_tess.m_curveTreeU[i]->index ) )->vertex );
				glDrawArrays( GL_LINE_STRIP, 0, GLsizei( m_tess.m_nArrayHeight ) );
			}

			p += m_tess.m_arrayWidth[i];
		}
	}
}
};

class RenderablePatchFixedWireframe : public OpenGLRenderable
{
PatchTesselation& m_tess;
public:
RenderablePatchFixedWireframe( PatchTesselation& tess ) : m_tess( tess ){
}
void render( RenderStateFlags state ) const {
	glVertexPointer( 3, GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->vertex );
	const RenderIndex* strip_indices = m_tess.m_indices.data();
	for ( std::size_t i = 0; i < m_tess.m_numStrips; i++, strip_indices += m_tess.m_lenStrips )
	{
		glDrawElements( GL_QUAD_STRIP, GLsizei( m_tess.m_lenStrips ), RenderIndexTypeID, strip_indices );
	}
}
};

class RenderablePatchSolid : public OpenGLRenderable
{
PatchTesselation& m_tess;
public:
RenderablePatchSolid( PatchTesselation& tess ) : m_tess( tess ){
}
void RenderNormals() const;
void render( RenderStateFlags state ) const {
#if 0
	if ( ( state & RENDER_FILL ) == 0 ) {
		RenderablePatchWireframe( m_tess ).render( state );
	}
	else
#endif
	{
		if ( ( state & RENDER_BUMP ) != 0 ) {
			if ( GlobalShaderCache().useShaderLanguage() ) {
				glNormalPointer( GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->normal );
				glVertexAttribPointerARB( c_attr_TexCoord0, 2, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->texcoord );
				glVertexAttribPointerARB( c_attr_Tangent, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->tangent );
				glVertexAttribPointerARB( c_attr_Binormal, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->bitangent );
			}
			else
			{
				glVertexAttribPointerARB( 11, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->normal );
				glVertexAttribPointerARB( 8, 2, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->texcoord );
				glVertexAttribPointerARB( 9, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->tangent );
				glVertexAttribPointerARB( 10, 3, GL_FLOAT, 0, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->bitangent );
			}
		}
		else
		{
			glNormalPointer( GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->normal );
			glTexCoordPointer( 2, GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->texcoord );
		}
		glVertexPointer( 3, GL_FLOAT, sizeof( ArbitraryMeshVertex ), &m_tess.m_vertices.data()->vertex );
		const RenderIndex* strip_indices = m_tess.m_indices.data();
		for ( std::size_t i = 0; i < m_tess.m_numStrips; i++, strip_indices += m_tess.m_lenStrips )
		{
			glDrawElements( GL_QUAD_STRIP, GLsizei( m_tess.m_lenStrips ), RenderIndexTypeID, strip_indices );
		}
	}

#if GDEF_DEBUG
	RenderNormals();
#endif
}
};

// parametric surface defined by quadratic bezier control curves
class Patch :
	public XMLImporter,
	public XMLExporter,
	public TransformNode,
	public Bounded,
	public Cullable,
	public Snappable,
	public Undoable,
	public Filterable,
	public Nameable
{
class xml_state_t
{
public:
enum EState
{
	eDefault,
	ePatch,
	eMatrix,
	eShader,
};
xml_state_t( EState state )
	: m_state( state )
{}
EState state() const {
	return m_state;
}
const char* content() const {
	return m_content.c_str();
}
std::size_t write( const char* buffer, std::size_t length ){
	return m_content.write( buffer, length );
}
private:
EState m_state;
StringOutputStream m_content;
};

std::vector<xml_state_t> m_xml_state;

typedef Array<PatchControl> PatchControlArray;

class SavedState : public UndoMemento
{
public:
SavedState(
	std::size_t width,
	std::size_t height,
	const PatchControlArray& ctrl,
	const char* shader,
	bool patchDef3,
	std::size_t subdivisions_x,
	std::size_t subdivisions_y
	) :
	m_width( width ),
	m_height( height ),
	m_shader( shader ),
	m_ctrl( ctrl ),
	m_patchDef3( patchDef3 ),
	m_subdivisions_x( subdivisions_x ),
	m_subdivisions_y( subdivisions_y ){
}

void release(){
	delete this;
}

std::size_t m_width, m_height;
CopiedString m_shader;
PatchControlArray m_ctrl;
bool m_patchDef3;
std::size_t m_subdivisions_x;
std::size_t m_subdivisions_y;
};

public:
class Observer
{
public:
virtual void allocate( std::size_t size ) = 0;
};

private:
typedef UniqueSet<Observer*> Observers;
Observers m_observers;

scene::Node* m_node;

AABB m_aabb_local;   // local bbox

CopiedString m_shader;
Shader* m_state;

std::size_t m_width;
std::size_t m_height;
public:
bool m_patchDef3;
std::size_t m_subdivisions_x;
std::size_t m_subdivisions_y;
private:

UndoObserver* m_undoable_observer;
MapFile* m_map;

// dynamically allocated array of control points, size is m_width*m_height
PatchControlArray m_ctrl;
PatchControlArray m_ctrlTransformed;

PatchTesselation m_tess;
RenderablePatchSolid m_render_solid;
RenderablePatchWireframe m_render_wireframe;
RenderablePatchFixedWireframe m_render_wireframe_fixed;

static Shader* m_state_ctrl;
static Shader* m_state_lattice;
VertexBuffer<PointVertex> m_ctrl_vertices;
RenderableVertexBuffer m_render_ctrl;
IndexBuffer m_lattice_indices;
RenderableIndexBuffer m_render_lattice;

bool m_bOverlay;

bool m_transformChanged;
Callback<void()> m_evaluateTransform;
Callback<void()> m_boundsChanged;

void construct(){
	m_bOverlay = false;
	m_width = m_height = 0;

	m_patchDef3 = false;
	m_subdivisions_x = 0;
	m_subdivisions_y = 0;

	check_shader();
	captureShader();

	m_xml_state.push_back( xml_state_t::eDefault );
}

public:
Callback<void()> m_lightsChanged;

static int m_CycleCapIndex;  // = 0;
static EPatchType m_type;

STRING_CONSTANT( Name, "Patch" );

Patch( scene::Node& node, const Callback<void()>& evaluateTransform, const Callback<void()>& boundsChanged ) :
	m_node( &node ),
	m_shader( texdef_name_default() ),
	m_state( 0 ),
	m_undoable_observer( 0 ),
	m_map( 0 ),
	m_render_solid( m_tess ),
	m_render_wireframe( m_tess ),
	m_render_wireframe_fixed( m_tess ),
	m_render_ctrl( GL_POINTS, m_ctrl_vertices ),
	m_render_lattice( GL_LINES, m_lattice_indices, m_ctrl_vertices ),
	m_transformChanged( false ),
	m_evaluateTransform( evaluateTransform ),
	m_boundsChanged( boundsChanged ){
	construct();
}
Patch( const Patch& other, scene::Node& node, const Callback<void()>& evaluateTransform, const Callback<void()>& boundsChanged ) :
	m_node( &node ),
	m_shader( texdef_name_default() ),
	m_state( 0 ),
	m_undoable_observer( 0 ),
	m_map( 0 ),
	m_render_solid( m_tess ),
	m_render_wireframe( m_tess ),
	m_render_wireframe_fixed( m_tess ),
	m_render_ctrl( GL_POINTS, m_ctrl_vertices ),
	m_render_lattice( GL_LINES, m_lattice_indices, m_ctrl_vertices ),
	m_transformChanged( false ),
	m_evaluateTransform( evaluateTransform ),
	m_boundsChanged( boundsChanged ){
	construct();

	m_patchDef3 = other.m_patchDef3;
	m_subdivisions_x = other.m_subdivisions_x;
	m_subdivisions_y = other.m_subdivisions_y;
	setDims( other.m_width, other.m_height );
	copy_ctrl( m_ctrl.data(), other.m_ctrl.data(), other.m_ctrl.data() + ( m_width * m_height ) );
	SetShader( other.m_shader.c_str() );
	controlPointsChanged();
}

Patch( const Patch& other ) :
	XMLImporter( other ),
	XMLExporter( other ),
	TransformNode( other ),
	Bounded( other ),
	Cullable( other ),
	Snappable(),
	Undoable( other ),
	Filterable( other ),
	Nameable( other ),
	m_state( 0 ),
	m_undoable_observer( 0 ),
	m_map( 0 ),
	m_render_solid( m_tess ),
	m_render_wireframe( m_tess ),
	m_render_wireframe_fixed( m_tess ),
	m_render_ctrl( GL_POINTS, m_ctrl_vertices ),
	m_render_lattice( GL_LINES, m_lattice_indices, m_ctrl_vertices ),
	m_transformChanged( false ),
	m_evaluateTransform( other.m_evaluateTransform ),
	m_boundsChanged( other.m_boundsChanged ){
	m_bOverlay = false;

	m_patchDef3 = other.m_patchDef3;
	m_subdivisions_x = other.m_subdivisions_x;
	m_subdivisions_y = other.m_subdivisions_y;
	setDims( other.m_width, other.m_height );
	copy_ctrl( m_ctrl.data(), other.m_ctrl.data(), other.m_ctrl.data() + ( m_width * m_height ) );
	SetShader( other.m_shader.c_str() );
	controlPointsChanged();
}

~Patch(){
	BezierCurveTreeArray_deleteAll( m_tess.m_curveTreeU );
	BezierCurveTreeArray_deleteAll( m_tess.m_curveTreeV );

	releaseShader();

	ASSERT_MESSAGE( m_observers.empty(), "Patch::~Patch: observers still attached" );
}

InstanceCounter m_instanceCounter;
void instanceAttach( const scene::Path& path ){
	if ( ++m_instanceCounter.m_count == 1 ) {
		m_state->incrementUsed();
		m_map = path_find_mapfile( path.begin(), path.end() );
		m_undoable_observer = GlobalUndoSystem().observer( this );
		GlobalFilterSystem().registerFilterable( *this );
	}
	else
	{
		ASSERT_MESSAGE( path_find_mapfile( path.begin(), path.end() ) == m_map, "node is instanced across more than one file" );
	}
}
void instanceDetach( const scene::Path& path ){
	if ( --m_instanceCounter.m_count == 0 ) {
		m_map = 0;
		m_undoable_observer = 0;
		GlobalUndoSystem().release( this );
		GlobalFilterSystem().unregisterFilterable( *this );
		m_state->decrementUsed();
	}
}

const char* name() const {
	return "patch";
}
void attach( const NameCallback& callback ){
}
void detach( const NameCallback& callback ){
}

void attach( Observer* observer ){
	observer->allocate( m_width * m_height );

	m_observers.insert( observer );
}
void detach( Observer* observer ){
	m_observers.erase( observer );
}

void updateFiltered(){
	if ( m_node != 0 ) {
		if ( patch_filtered( *this ) ) {
			m_node->enable( scene::Node::eFiltered );
		}
		else
		{
			m_node->disable( scene::Node::eFiltered );
		}
	}
}

void onAllocate( std::size_t size ){
	for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
	{
		( *i )->allocate( size );
	}
}

const Matrix4& localToParent() const {
	return g_matrix4_identity;
}
const AABB& localAABB() const {
	return m_aabb_local;
}
VolumeIntersectionValue intersectVolume( const VolumeTest& test, const Matrix4& localToWorld ) const {
	return test.TestAABB( m_aabb_local, localToWorld );
}
void render_solid( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
	renderer.SetState( m_state, Renderer::eFullMaterials );
	renderer.addRenderable( m_render_solid, localToWorld );
}
void render_wireframe( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
	renderer.SetState( m_state, Renderer::eFullMaterials );
	if ( m_patchDef3 ) {
		renderer.addRenderable( m_render_wireframe_fixed, localToWorld );
	}
	else
	{
		renderer.addRenderable( m_render_wireframe, localToWorld );
	}
}

void render_component( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
	renderer.SetState( m_state_lattice, Renderer::eWireframeOnly );
	renderer.SetState( m_state_lattice, Renderer::eFullMaterials );
	renderer.addRenderable( m_render_lattice, localToWorld );

	renderer.SetState( m_state_ctrl, Renderer::eWireframeOnly );
	renderer.SetState( m_state_ctrl, Renderer::eFullMaterials );
	renderer.addRenderable( m_render_ctrl, localToWorld );
}
void testSelect( Selector& selector, SelectionTest& test ){
	SelectionIntersection best;
	IndexPointer::index_type* pIndex = m_tess.m_indices.data();
	for ( std::size_t s = 0; s < m_tess.m_numStrips; s++ )
	{
		test.TestQuadStrip( vertexpointer_arbitrarymeshvertex( m_tess.m_vertices.data() ), IndexPointer( pIndex, m_tess.m_lenStrips ), best );
		pIndex += m_tess.m_lenStrips;
	}
	if ( best.valid() ) {
		selector.addIntersection( best );
	}
}
void transform( const Matrix4& matrix ){
	for ( PatchControlIter i = m_ctrlTransformed.data(); i != m_ctrlTransformed.data() + m_ctrlTransformed.size(); ++i )
	{
		matrix4_transform_point( matrix, ( *i ).m_vertex );
	}

	if ( matrix4_handedness( matrix ) == MATRIX4_LEFTHANDED ) {
		PatchControlArray_invert( m_ctrlTransformed, m_width, m_height );
	}
	UpdateCachedData();
}
void transformChanged(){
	m_transformChanged = true;
	m_lightsChanged();
	SceneChangeNotify();
}
typedef MemberCaller<Patch, void(), &Patch::transformChanged> TransformChangedCaller;

void evaluateTransform(){
	if ( m_transformChanged ) {
		m_transformChanged = false;
		revertTransform();
		m_evaluateTransform();
	}
}

void revertTransform(){
	m_ctrlTransformed = m_ctrl;
}
void freezeTransform(){
	undoSave();
	evaluateTransform();
	ASSERT_MESSAGE( m_ctrlTransformed.size() == m_ctrl.size(), "Patch::freeze: size mismatch" );
	std::copy( m_ctrlTransformed.begin(), m_ctrlTransformed.end(), m_ctrl.begin() );
}

void controlPointsChanged(){
	transformChanged();
	evaluateTransform();
	UpdateCachedData();
}
bool isValid() const;

void snapto( float snap ){
	undoSave();

	for ( PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i )
	{
		vector3_snap( ( *i ).m_vertex, snap );
	}

	controlPointsChanged();
}




void RenderDebug( RenderStateFlags state ) const;
void RenderNormals( RenderStateFlags state ) const;

void pushElement( const XMLElement& element ){
	switch ( m_xml_state.back().state() )
	{
	case xml_state_t::eDefault:
		ASSERT_MESSAGE( string_equal( element.name(), "patch" ), "parse error" );
		m_xml_state.push_back( xml_state_t::ePatch );
		break;
	case xml_state_t::ePatch:
		if ( string_equal( element.name(), "matrix" ) ) {
			setDims( atoi( element.attribute( "width" ) ), atoi( element.attribute( "height" ) ) );
			m_xml_state.push_back( xml_state_t::eMatrix );
		}
		else if ( string_equal( element.name(), "shader" ) ) {
			m_xml_state.push_back( xml_state_t::eShader );
		}
		break;
	default:
		ERROR_MESSAGE( "parse error" );
	}

}
void popElement( const char* name ){
	switch ( m_xml_state.back().state() )
	{
	case xml_state_t::eDefault:
		ERROR_MESSAGE( "parse error" );
		break;
	case xml_state_t::ePatch:
		break;
	case xml_state_t::eMatrix:
	{
		StringTokeniser content( m_xml_state.back().content() );

		for ( PatchControlIter i = m_ctrl.data(), end = m_ctrl.data() + m_ctrl.size(); i != end; ++i )
		{
			( *i ).m_vertex[0] = string_read_float( content.getToken() );
			( *i ).m_vertex[1] = string_read_float( content.getToken() );
			( *i ).m_vertex[2] = string_read_float( content.getToken() );
			( *i ).m_texcoord[0] = string_read_float( content.getToken() );
			( *i ).m_texcoord[1] = string_read_float( content.getToken() );
		}
		controlPointsChanged();
	}
	break;
	case xml_state_t::eShader:
	{
		SetShader( m_xml_state.back().content() );
	}
	break;
	default:
		ERROR_MESSAGE( "parse error" );
	}

	ASSERT_MESSAGE( !m_xml_state.empty(), "popping empty stack" );
	m_xml_state.pop_back();
}
std::size_t write( const char* buffer, std::size_t length ){
	switch ( m_xml_state.back().state() )
	{
	case xml_state_t::eDefault:
		break;
	case xml_state_t::ePatch:
		break;
	case xml_state_t::eMatrix:
	case xml_state_t::eShader:
		return m_xml_state.back().write( buffer, length );
		break;
	default:
		ERROR_MESSAGE( "parse error" );
	}
	return length;
}

void exportXML( XMLImporter& importer ){
	StaticElement patchElement( "patch" );
	importer.pushElement( patchElement );

	{
		const StaticElement element( "shader" );
		importer.pushElement( element );
		importer.write( m_shader.c_str(), strlen( m_shader.c_str() ) );
		importer.popElement( element.name() );
	}

	{
		char width[16], height[16];
		sprintf( width, "%u", Unsigned( m_width ) );
		sprintf( height, "%u", Unsigned( m_height ) );
		StaticElement element( "matrix" );
		element.insertAttribute( "width", width );
		element.insertAttribute( "height", height );

		importer.pushElement( element );
		{
			for ( PatchControlIter i = m_ctrl.data(), end = m_ctrl.data() + m_ctrl.size(); i != end; ++i )
			{
				importer << ( *i ).m_vertex[0]
						 << ' ' << ( *i ).m_vertex[1]
						 << ' ' << ( *i ).m_vertex[2]
						 << ' ' << ( *i ).m_texcoord[0]
						 << ' ' << ( *i ).m_texcoord[1];
			}
		}
		importer.popElement( element.name() );
	}

	importer.popElement( patchElement.name() );
}

void UpdateCachedData();

const char *GetShader() const {
	return m_shader.c_str();
}
void SetShader( const char* name ){
	ASSERT_NOTNULL( name );

	if ( shader_equal( m_shader.c_str(), name ) ) {
		return;
	}

	undoSave();

	if ( m_instanceCounter.m_count != 0 ) {
		m_state->decrementUsed();
	}
	releaseShader();
	m_shader = name;
	captureShader();
	if ( m_instanceCounter.m_count != 0 ) {
		m_state->incrementUsed();
	}

	check_shader();
	Patch_textureChanged();
}
int getShaderFlags() const {
	if ( m_state != 0 ) {
		return m_state->getFlags();
	}
	return 0;
}

typedef PatchControl* iterator;
typedef const PatchControl* const_iterator;

iterator begin(){
	return m_ctrl.data();
}
const_iterator begin() const {
	return m_ctrl.data();
}
iterator end(){
	return m_ctrl.data() + m_ctrl.size();
}
const_iterator end() const {
	return m_ctrl.data() + m_ctrl.size();
}

PatchControlArray& getControlPoints(){
	return m_ctrl;
}

// Same as above, just for const arguments
const PatchControlArray& getControlPoints() const {
	return m_ctrl;
}

PatchControlArray& getControlPointsTransformed(){
	return m_ctrlTransformed;
}

void setDims( std::size_t w, std::size_t h );
std::size_t getWidth() const {
	return m_width;
}
std::size_t getHeight() const {
	return m_height;
}
PatchControl& ctrlAt( std::size_t row, std::size_t col ){
	return m_ctrl[row * m_width + col];
}
const PatchControl& ctrlAt( std::size_t row, std::size_t col ) const {
	return m_ctrl[row * m_width + col];
}

void ConstructPrefab( const AABB& aabb, EPatchPrefab eType, int axis, std::size_t width = 3, std::size_t height = 3 );
void constructPlane( const AABB& aabb, int axis, std::size_t width, std::size_t height );
void InvertMatrix();
void TransposeMatrix();
void Redisperse( EMatrixMajor mt );
void Smooth( EMatrixMajor mt );
void InsertRemove( bool bInsert, bool bColumn, bool bFirst );
Patch* MakeCap( Patch* patch, EPatchCap eType, EMatrixMajor mt, bool bFirst );
void ConstructSeam( EPatchCap eType, Vector3* p, std::size_t width );

void FlipTexture( int nAxis );
void TranslateTexture( float s, float t );
void ScaleTexture( float s, float t );
void RotateTexture( float angle );
void SetTextureRepeat( float s, float t ); // call with s=1 t=1 for FIT
void CapTexture();
void NaturalTexture();
void ProjectTexture( int nAxis );
void createThickenedOpposite(const Patch& sourcePatch, const float thickness, const int axis, bool& no12, bool& no34 );
void createThickenedWall(const Patch& sourcePatch, const Patch& targetPatch, const int wallIndex);

void undoSave(){
	if ( m_map != 0 ) {
		m_map->changed();
	}
	if ( m_undoable_observer != 0 ) {
		m_undoable_observer->save( this );
	}
}

UndoMemento* exportState() const {
	return new SavedState( m_width, m_height, m_ctrl, m_shader.c_str(), m_patchDef3, m_subdivisions_x, m_subdivisions_y );
}
void importState( const UndoMemento* state ){
	undoSave();

	const SavedState& other = *( static_cast<const SavedState*>( state ) );

	// begin duplicate of SavedState copy constructor, needs refactoring

	// copy construct
	{
		m_width = other.m_width;
		m_height = other.m_height;
		SetShader( other.m_shader.c_str() );
		m_ctrl = other.m_ctrl;
		onAllocate( m_ctrl.size() );
		m_patchDef3 = other.m_patchDef3;
		m_subdivisions_x = other.m_subdivisions_x;
		m_subdivisions_y = other.m_subdivisions_y;
	}

	// end duplicate code

	Patch_textureChanged();

	controlPointsChanged();
}

static void constructStatic( EPatchType type ){
	Patch::m_type = type;
	Patch::m_state_ctrl = GlobalShaderCache().capture( "$POINT" );
	Patch::m_state_lattice = GlobalShaderCache().capture( "$LATTICE" );
}

static void destroyStatic(){
	GlobalShaderCache().release( "$LATTICE" );
	GlobalShaderCache().release( "$POINT" );
}
private:
void captureShader(){
	m_state = GlobalShaderCache().capture( m_shader.c_str() );
}

void releaseShader(){
	GlobalShaderCache().release( m_shader.c_str() );
}

void check_shader(){
	if ( !shader_valid( GetShader() ) ) {
		globalErrorStream() << "patch has invalid texture name: '" << GetShader() << "'\n";
	}
}

void InsertPoints( EMatrixMajor mt, bool bFirst );
void RemovePoints( EMatrixMajor mt, bool bFirst );

void AccumulateBBox();

void TesselateSubMatrixFixed( ArbitraryMeshVertex * vertices, std::size_t strideX, std::size_t strideY, unsigned int nFlagsX, unsigned int nFlagsY, PatchControl * subMatrix[3][3] );

// uses binary trees representing bezier curves to recursively tesselate a bezier sub-patch
void TesselateSubMatrix( const BezierCurveTree *BX, const BezierCurveTree *BY,
						 std::size_t offStartX, std::size_t offStartY,
						 std::size_t offEndX, std::size_t offEndY,
						 std::size_t nFlagsX, std::size_t nFlagsY,
						 Vector3& left, Vector3& mid, Vector3& right,
						 Vector2& texLeft, Vector2& texMid, Vector2& texRight,
						 bool bTranspose );

// tesselates the entire surface
void BuildTesselationCurves( EMatrixMajor major );
void accumulateVertexTangentSpace( std::size_t index, Vector3 tangentX[6], Vector3 tangentY[6], Vector2 tangentS[6], Vector2 tangentT[6], std::size_t index0, std::size_t index1 );
void BuildVertexArray();
};

inline bool Patch_importHeader( Patch& patch, Tokeniser& tokeniser ){
	tokeniser.nextLine();
	RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "{" ) );
	return true;
}

inline bool Patch_importShader( Patch& patch, Tokeniser& tokeniser ){
	// parse shader name
	tokeniser.nextLine();
	const char* texture = tokeniser.getToken();
	if ( texture == 0 ) {
		Tokeniser_unexpectedError( tokeniser, texture, "#texture-name" );
		return false;
	}
	if ( string_equal( texture, "NULL" ) ) {
		patch.SetShader( texdef_name_default() );
	}
	else
	{
		StringOutputStream shader( string_length( GlobalTexturePrefix_get() ) + string_length( texture ) );
		shader << GlobalTexturePrefix_get() << texture;
		patch.SetShader( shader.c_str() );
	}
	return true;
}

inline bool PatchDoom3_importShader( Patch& patch, Tokeniser& tokeniser ){
	// parse shader name
	tokeniser.nextLine();
	const char *shader = tokeniser.getToken();
	if ( shader == 0 ) {
		Tokeniser_unexpectedError( tokeniser, shader, "#shader-name" );
		return false;
	}
	if ( string_equal( shader, "_emptyname" ) ) {
		shader = texdef_name_default();
	}
	patch.SetShader( shader );
	return true;
}

inline bool Patch_importParams( Patch& patch, Tokeniser& tokeniser ){
	tokeniser.nextLine();
	RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) );

	// parse matrix dimensions
	{
		std::size_t c, r;
		RETURN_FALSE_IF_FAIL( Tokeniser_getSize( tokeniser, c ) );
		RETURN_FALSE_IF_FAIL( Tokeniser_getSize( tokeniser, r ) );

		patch.setDims( c, r );
	}

	if ( patch.m_patchDef3 ) {
		RETURN_FALSE_IF_FAIL( Tokeniser_getSize( tokeniser, patch.m_subdivisions_x ) );
		RETURN_FALSE_IF_FAIL( Tokeniser_getSize( tokeniser, patch.m_subdivisions_y ) );
	}

	// ignore contents/flags/value
	int tmp;
	RETURN_FALSE_IF_FAIL( Tokeniser_getInteger( tokeniser, tmp ) );
	RETURN_FALSE_IF_FAIL( Tokeniser_getInteger( tokeniser, tmp ) );
	RETURN_FALSE_IF_FAIL( Tokeniser_getInteger( tokeniser, tmp ) );

	RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) );
	return true;
}

inline bool Patch_importMatrix( Patch& patch, Tokeniser& tokeniser ){
	// parse matrix
	tokeniser.nextLine();
	RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) );
	{
		for ( std::size_t c = 0; c < patch.getWidth(); c++ )
		{
			tokeniser.nextLine();
			RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) );
			for ( std::size_t r = 0; r < patch.getHeight(); r++ )
			{
				RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) );

				RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, patch.ctrlAt( r,c ).m_vertex[0] ) );
				RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, patch.ctrlAt( r,c ).m_vertex[1] ) );
				RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, patch.ctrlAt( r,c ).m_vertex[2] ) );
				RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, patch.ctrlAt( r,c ).m_texcoord[0] ) );
				RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, patch.ctrlAt( r,c ).m_texcoord[1] ) );

				RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) );
			}
			RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) );
		}
	}
	tokeniser.nextLine();
	RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) );
	return true;
}

inline bool Patch_importFooter( Patch& patch, Tokeniser& tokeniser ){
	patch.controlPointsChanged();

	tokeniser.nextLine();
	RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "}" ) );

	tokeniser.nextLine();
	RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "}" ) );
	return true;
}

class PatchTokenImporter : public MapImporter
{
Patch& m_patch;
public:
PatchTokenImporter( Patch& patch ) : m_patch( patch ){
}
bool importTokens( Tokeniser& tokeniser ){
	RETURN_FALSE_IF_FAIL( Patch_importHeader( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( Patch_importShader( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( Patch_importParams( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( Patch_importMatrix( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( Patch_importFooter( m_patch, tokeniser ) );

	return true;
}
};

class PatchDoom3TokenImporter : public MapImporter
{
Patch& m_patch;
public:
PatchDoom3TokenImporter( Patch& patch ) : m_patch( patch ){
}
bool importTokens( Tokeniser& tokeniser ){
	RETURN_FALSE_IF_FAIL( Patch_importHeader( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( PatchDoom3_importShader( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( Patch_importParams( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( Patch_importMatrix( m_patch, tokeniser ) );
	RETURN_FALSE_IF_FAIL( Patch_importFooter( m_patch, tokeniser ) );

	return true;
}
};

inline void Patch_exportHeader( const Patch& patch, TokenWriter& writer ){
	writer.writeToken( "{" );
	writer.nextLine();
	writer.writeToken( patch.m_patchDef3 ? "patchDef3" : "patchDef2" );
	writer.nextLine();
	writer.writeToken( "{" );
	writer.nextLine();
}

inline void Patch_exportShader( const Patch& patch, TokenWriter& writer ){
	// write shader name
	if ( *( shader_get_textureName( patch.GetShader() ) ) == '\0' ) {
		writer.writeToken( "NULL" );
	}
	else
	{
		writer.writeToken( shader_get_textureName( patch.GetShader() ) );
	}
	writer.nextLine();
}

inline void PatchDoom3_exportShader( const Patch& patch, TokenWriter& writer ){
	// write shader name
	if ( *( shader_get_textureName( patch.GetShader() ) ) == '\0' ) {
		writer.writeString( "_emptyname" );
	}
	else
	{
		writer.writeString( patch.GetShader() );
	}
	writer.nextLine();
}

inline void Patch_exportParams( const Patch& patch, TokenWriter& writer ){
	// write matrix dimensions
	writer.writeToken( "(" );
	writer.writeUnsigned( patch.getWidth() );
	writer.writeUnsigned( patch.getHeight() );
	if ( patch.m_patchDef3 ) {
		writer.writeUnsigned( patch.m_subdivisions_x );
		writer.writeUnsigned( patch.m_subdivisions_y );
	}
	writer.writeInteger( 0 );
	writer.writeInteger( 0 );
	writer.writeInteger( 0 );
	writer.writeToken( ")" );
	writer.nextLine();
}

inline void Patch_exportMatrix( const Patch& patch, TokenWriter& writer ){
	// write matrix
	writer.writeToken( "(" );
	writer.nextLine();
	for ( std::size_t c = 0; c < patch.getWidth(); c++ )
	{
		writer.writeToken( "(" );
		for ( std::size_t r = 0; r < patch.getHeight(); r++ )
		{
			writer.writeToken( "(" );

			writer.writeFloat( patch.ctrlAt( r,c ).m_vertex[0] );
			writer.writeFloat( patch.ctrlAt( r,c ).m_vertex[1] );
			writer.writeFloat( patch.ctrlAt( r,c ).m_vertex[2] );
			writer.writeFloat( patch.ctrlAt( r,c ).m_texcoord[0] );
			writer.writeFloat( patch.ctrlAt( r,c ).m_texcoord[1] );

			writer.writeToken( ")" );
		}
		writer.writeToken( ")" );
		writer.nextLine();
	}
	writer.writeToken( ")" );
	writer.nextLine();
}

inline void Patch_exportFooter( const Patch& patch, TokenWriter& writer ){
	writer.writeToken( "}" );
	writer.nextLine();
	writer.writeToken( "}" );
	writer.nextLine();
}

class PatchTokenExporter : public MapExporter
{
const Patch& m_patch;
public:
PatchTokenExporter( Patch& patch ) : m_patch( patch ){
}
void exportTokens( TokenWriter& writer ) const {
	Patch_exportHeader( m_patch, writer );
	Patch_exportShader( m_patch, writer );
	Patch_exportParams( m_patch, writer );
	Patch_exportMatrix( m_patch, writer );
	Patch_exportFooter( m_patch, writer );
}
};

class PatchDoom3TokenExporter : public MapExporter
{
const Patch& m_patch;
public:
PatchDoom3TokenExporter( Patch& patch ) : m_patch( patch ){
}
void exportTokens( TokenWriter& writer ) const {
	Patch_exportHeader( m_patch, writer );
	PatchDoom3_exportShader( m_patch, writer );
	Patch_exportParams( m_patch, writer );
	Patch_exportMatrix( m_patch, writer );
	Patch_exportFooter( m_patch, writer );
}
};

class PatchControlInstance
{
public:
PatchControl* m_ctrl;
ObservedSelectable m_selectable;

PatchControlInstance( PatchControl* ctrl, const SelectionChangeCallback& observer )
	: m_ctrl( ctrl ), m_selectable( observer ){
}

void testSelect( Selector& selector, SelectionTest& test ){
	SelectionIntersection best;
	test.TestPoint( m_ctrl->m_vertex, best );
	if ( best.valid() ) {
		Selector_add( selector, m_selectable, best );
	}
}
void snapto( float snap ){
	vector3_snap( m_ctrl->m_vertex, snap );
}
};


class PatchInstance :
	public Patch::Observer,
	public scene::Instance,
	public Selectable,
	public Renderable,
	public SelectionTestable,
	public ComponentSelectionTestable,
	public ComponentEditable,
	public ComponentSnappable,
	public PlaneSelectable,
	public LightCullable
{
class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts(){
	InstanceStaticCast<PatchInstance, Selectable>::install( m_casts );
	InstanceContainedCast<PatchInstance, Bounded>::install( m_casts );
	InstanceContainedCast<PatchInstance, Cullable>::install( m_casts );
	InstanceStaticCast<PatchInstance, Renderable>::install( m_casts );
	InstanceStaticCast<PatchInstance, SelectionTestable>::install( m_casts );
	InstanceStaticCast<PatchInstance, ComponentSelectionTestable>::install( m_casts );
	InstanceStaticCast<PatchInstance, ComponentEditable>::install( m_casts );
	InstanceStaticCast<PatchInstance, ComponentSnappable>::install( m_casts );
	InstanceStaticCast<PatchInstance, PlaneSelectable>::install( m_casts );
	InstanceIdentityCast<PatchInstance>::install( m_casts );
	InstanceContainedCast<PatchInstance, Transformable>::install( m_casts );
}
InstanceTypeCastTable& get(){
	return m_casts;
}
};


Patch& m_patch;
typedef std::vector<PatchControlInstance> PatchControlInstances;
PatchControlInstances m_ctrl_instances;

ObservedSelectable m_selectable;

DragPlanes m_dragPlanes;

mutable RenderablePointVector m_render_selected;
mutable AABB m_aabb_component;

static Shader* m_state_selpoint;

const LightList* m_lightList;

TransformModifier m_transform;
public:

typedef LazyStatic<TypeCasts> StaticTypeCasts;

void lightsChanged(){
	m_lightList->lightsChanged();
}
typedef MemberCaller<PatchInstance, void(), &PatchInstance::lightsChanged> LightsChangedCaller;

STRING_CONSTANT( Name, "PatchInstance" );

PatchInstance( const scene::Path& path, scene::Instance* parent, Patch& patch ) :
	Instance( path, parent, this, StaticTypeCasts::instance().get() ),
	m_patch( patch ),
	m_selectable( SelectedChangedCaller( *this ) ),
	m_dragPlanes( SelectedChangedComponentCaller( *this ) ),
	m_render_selected( GL_POINTS ),
	m_transform( Patch::TransformChangedCaller( m_patch ), ApplyTransformCaller( *this ) ){
	m_patch.instanceAttach( Instance::path() );
	m_patch.attach( this );

	m_lightList = &GlobalShaderCache().attach( *this );
	m_patch.m_lightsChanged = LightsChangedCaller( *this );

	Instance::setTransformChangedCallback( LightsChangedCaller( *this ) );
}
~PatchInstance(){
	Instance::setTransformChangedCallback( Callback<void()>() );

	m_patch.m_lightsChanged = Callback<void()>();
	GlobalShaderCache().detach( *this );

	m_patch.detach( this );
	m_patch.instanceDetach( Instance::path() );
}

void selectedChanged( const Selectable& selectable ){
	GlobalSelectionSystem().getObserver ( SelectionSystem::ePrimitive )( selectable );
	GlobalSelectionSystem().onSelectedChanged( *this, selectable );

	Instance::selectedChanged();
}
typedef MemberCaller<PatchInstance, void(const Selectable&), &PatchInstance::selectedChanged> SelectedChangedCaller;

void selectedChangedComponent( const Selectable& selectable ){
	GlobalSelectionSystem().getObserver ( SelectionSystem::eComponent )( selectable );
	GlobalSelectionSystem().onComponentSelection( *this, selectable );
}
typedef MemberCaller<PatchInstance, void(const Selectable&), &PatchInstance::selectedChangedComponent> SelectedChangedComponentCaller;

Patch& getPatch(){
	return m_patch;
}
Bounded& get( NullType<Bounded>){
	return m_patch;
}
Cullable& get( NullType<Cullable>){
	return m_patch;
}
Transformable& get( NullType<Transformable>){
	return m_transform;
}

static void constructStatic(){
	m_state_selpoint = GlobalShaderCache().capture( "$SELPOINT" );
}

static void destroyStatic(){
	GlobalShaderCache().release( "$SELPOINT" );
}


void allocate( std::size_t size ){
	m_ctrl_instances.clear();
	m_ctrl_instances.reserve( size );
	for ( Patch::iterator i = m_patch.begin(); i != m_patch.end(); ++i )
	{
		m_ctrl_instances.push_back( PatchControlInstance( &( *i ), SelectedChangedComponentCaller( *this ) ) );
	}
}

void setSelected( bool select ){
	m_selectable.setSelected( select );
	if ( !select && parent() ){
		Selectable* sel_parent = Instance_getSelectable( *parent() );
		if ( sel_parent && sel_parent->isSelected() )
			sel_parent->setSelected( false );
	}
}
bool isSelected() const {
	return m_selectable.isSelected();
}


void update_selected() const {
	m_render_selected.clear();
	Patch::iterator ctrl = m_patch.getControlPointsTransformed().begin();
	for ( PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i, ++ctrl )
	{
		if ( ( *i ).m_selectable.isSelected() ) {
			const Colour4b colour_selected( 0, 0, 255, 255 );
			m_render_selected.push_back( PointVertex( reinterpret_cast<Vertex3f&>( ( *ctrl ).m_vertex ), colour_selected ) );
		}
	}
}

#if 0
void render( Renderer& renderer, const VolumeTest& volume ) const {
	if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent
		 && m_selectable.isSelected() ) {
		renderer.Highlight( Renderer::eFace, false );

		m_patch.render( renderer, volume, localToWorld() );

		if ( GlobalSelectionSystem().ComponentMode() == SelectionSystem::eVertex ) {
			renderer.Highlight( Renderer::ePrimitive, false );

			m_patch.render_component( renderer, volume, localToWorld() );

			renderComponentsSelected( renderer, volume );
		}
	}
	else{
		m_patch.render( renderer, volume, localToWorld() );
	}
}
#endif

void renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
	m_patch.evaluateTransform();
	renderer.setLights( *m_lightList );
	m_patch.render_solid( renderer, volume, localToWorld() );

	renderComponentsSelected( renderer, volume );
}

void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const {
	m_patch.evaluateTransform();
	m_patch.render_wireframe( renderer, volume, localToWorld() );

	renderComponentsSelected( renderer, volume );
}

void renderComponentsSelected( Renderer& renderer, const VolumeTest& volume ) const {
	m_patch.evaluateTransform();
	update_selected();
	if ( !m_render_selected.empty() ) {
		renderer.Highlight( Renderer::ePrimitive, false );
		renderer.SetState( m_state_selpoint, Renderer::eWireframeOnly );
		renderer.SetState( m_state_selpoint, Renderer::eFullMaterials );
		renderer.addRenderable( m_render_selected, localToWorld() );
	}
}
void renderComponents( Renderer& renderer, const VolumeTest& volume ) const {
	m_patch.evaluateTransform();
	if ( GlobalSelectionSystem().ComponentMode() == SelectionSystem::eVertex ) {
		m_patch.render_component( renderer, volume, localToWorld() );
	}
}

void testSelect( Selector& selector, SelectionTest& test ){
	test.BeginMesh( localToWorld(), true );
	m_patch.testSelect( selector, test );
}

void selectCtrl( bool select ){
	for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
	{
		( *i ).m_selectable.setSelected( select );
	}
}
bool isSelectedComponents() const {
	for ( PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
	{
		if ( ( *i ).m_selectable.isSelected() ) {
			return true;
		}
	}
	return false;
}
void setSelectedComponents( bool select, SelectionSystem::EComponentMode mode ){
	if ( mode == SelectionSystem::eVertex ) {
		selectCtrl( select );
	}
	else if ( mode == SelectionSystem::eFace ) {
		m_dragPlanes.setSelected( select );
	}
}
const AABB& getSelectedComponentsBounds() const {
	m_aabb_component = AABB();

	for ( PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
	{
		if ( ( *i ).m_selectable.isSelected() ) {
			aabb_extend_by_point_safe( m_aabb_component, ( *i ).m_ctrl->m_vertex );
		}
	}

	return m_aabb_component;
}

void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ){
	test.BeginMesh( localToWorld() );

	switch ( mode )
	{
	case SelectionSystem::eVertex:
	{
		for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
		{
			( *i ).testSelect( selector, test );
		}
	}
	break;
	default:
		break;
	}
}

bool selectedVertices(){
	for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
	{
		if ( ( *i ).m_selectable.isSelected() ) {
			return true;
		}
	}
	return false;
}

void transformComponents( const Matrix4& matrix ){
	if ( selectedVertices() ) {
		PatchControlIter ctrl = m_patch.getControlPointsTransformed().begin();
		for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i, ++ctrl )
		{
			if ( ( *i ).m_selectable.isSelected() ) {
				matrix4_transform_point( matrix, ( *ctrl ).m_vertex );
			}
		}
		m_patch.UpdateCachedData();
	}

	if ( m_dragPlanes.isSelected() ) { // this should only be true when the transform is a pure translation.
		m_patch.transform( m_dragPlanes.evaluateTransform( vector4_to_vector3( matrix.t() ) ) );
	}
}

void invertComponentSelection(){
	for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
	{
		( *i ).m_selectable.setSelected( !( *i ).m_selectable.isSelected() );
	}
}


void selectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback ){
	test.BeginMesh( localToWorld() );

	m_dragPlanes.selectPlanes( m_patch.localAABB(), selector, test, selectedPlaneCallback );
}
void selectReversedPlanes( Selector& selector, const SelectedPlanes& selectedPlanes ){
	m_dragPlanes.selectReversedPlanes( m_patch.localAABB(), selector, selectedPlanes );
}


void snapComponents( float snap ){
	if ( selectedVertices() ) {
		m_patch.undoSave();
		for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
		{
			if ( ( *i ).m_selectable.isSelected() ) {
				( *i ).snapto( snap );
			}
		}
		m_patch.controlPointsChanged();
	}
}

void evaluateTransform(){
	Matrix4 matrix( m_transform.calculateTransform() );

	if ( m_transform.getType() == TRANSFORM_PRIMITIVE ) {
		m_patch.transform( matrix );
	}
	else
	{
		transformComponents( matrix );
	}
}
void applyTransform(){
	m_patch.revertTransform();
	evaluateTransform();
	m_patch.freezeTransform();
}
typedef MemberCaller<PatchInstance, void(), &PatchInstance::applyTransform> ApplyTransformCaller;


bool testLight( const RendererLight& light ) const {
	return light.testAABB( worldAABB() );
}
};


template<typename TokenImporter, typename TokenExporter>
class PatchNode :
	public scene::Node::Symbiot,
	public scene::Instantiable,
	public scene::Cloneable
{
typedef PatchNode<TokenImporter, TokenExporter> Self;

class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts(){
	NodeStaticCast<PatchNode, scene::Instantiable>::install( m_casts );
	NodeStaticCast<PatchNode, scene::Cloneable>::install( m_casts );
	NodeContainedCast<PatchNode, Snappable>::install( m_casts );
	NodeContainedCast<PatchNode, TransformNode>::install( m_casts );
	NodeContainedCast<PatchNode, Patch>::install( m_casts );
	NodeContainedCast<PatchNode, XMLImporter>::install( m_casts );
	NodeContainedCast<PatchNode, XMLExporter>::install( m_casts );
	NodeContainedCast<PatchNode, MapImporter>::install( m_casts );
	NodeContainedCast<PatchNode, MapExporter>::install( m_casts );
	NodeContainedCast<PatchNode, Nameable>::install( m_casts );
}
InstanceTypeCastTable& get(){
	return m_casts;
}
};


scene::Node m_node;
InstanceSet m_instances;
Patch m_patch;
TokenImporter m_importMap;
TokenExporter m_exportMap;

public:

typedef LazyStatic<TypeCasts> StaticTypeCasts;

Snappable& get( NullType<Snappable>){
	return m_patch;
}
TransformNode& get( NullType<TransformNode>){
	return m_patch;
}
Patch& get( NullType<Patch>){
	return m_patch;
}
XMLImporter& get( NullType<XMLImporter>){
	return m_patch;
}
XMLExporter& get( NullType<XMLExporter>){
	return m_patch;
}
MapImporter& get( NullType<MapImporter>){
	return m_importMap;
}
MapExporter& get( NullType<MapExporter>){
	return m_exportMap;
}
Nameable& get( NullType<Nameable>){
	return m_patch;
}

PatchNode( bool patchDef3 = false ) :
	m_node( this, this, StaticTypeCasts::instance().get() ),
	m_patch( m_node, InstanceSetEvaluateTransform<PatchInstance>::Caller( m_instances ), InstanceSet::BoundsChangedCaller( m_instances ) ),
	m_importMap( m_patch ),
	m_exportMap( m_patch ){
	m_patch.m_patchDef3 = patchDef3;
}
PatchNode( const PatchNode& other ) :
	scene::Node::Symbiot( other ),
	scene::Instantiable( other ),
	scene::Cloneable( other ),
	m_node( this, this, StaticTypeCasts::instance().get() ),
	m_patch( other.m_patch, m_node, InstanceSetEvaluateTransform<PatchInstance>::Caller( m_instances ), InstanceSet::BoundsChangedCaller( m_instances ) ),
	m_importMap( m_patch ),
	m_exportMap( m_patch ){
}
void release(){
	delete this;
}
scene::Node& node(){
	return m_node;
}
Patch& get(){
	return m_patch;
}
const Patch& get() const {
	return m_patch;
}

scene::Node& clone() const {
	return ( new PatchNode( *this ) )->node();
}

scene::Instance* create( const scene::Path& path, scene::Instance* parent ){
	return new PatchInstance( path, parent, m_patch );
}
void forEachInstance( const scene::Instantiable::Visitor& visitor ){
	m_instances.forEachInstance( visitor );
}
void insert( scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance ){
	m_instances.insert( observer, path, instance );
}
scene::Instance* erase( scene::Instantiable::Observer* observer, const scene::Path& path ){
	return m_instances.erase( observer, path );
}
};



typedef PatchNode<PatchTokenImporter, PatchTokenExporter> PatchNodeQuake3;
typedef PatchNode<PatchDoom3TokenImporter, PatchDoom3TokenExporter> PatchNodeDoom3;

inline Patch* Node_getPatch( scene::Node& node ){
	return NodeTypeCast<Patch>::cast( node );
}

inline PatchInstance* Instance_getPatch( scene::Instance& instance ){
	return InstanceTypeCast<PatchInstance>::cast( instance );
}

template<typename Functor>
class PatchSelectedVisitor : public SelectionSystem::Visitor
{
const Functor& m_functor;
public:
PatchSelectedVisitor( const Functor& functor ) : m_functor( functor ){
}
void visit( scene::Instance& instance ) const {
	PatchInstance* patch = Instance_getPatch( instance );
	if ( patch != 0 ) {
		m_functor( *patch );
	}
}
};

template<typename Functor>
inline void Scene_forEachSelectedPatch( const Functor& functor ){
	GlobalSelectionSystem().foreachSelected( PatchSelectedVisitor<Functor>( functor ) );
}


template<typename Functor>
class PatchVisibleSelectedVisitor : public SelectionSystem::Visitor
{
const Functor& m_functor;
public:
PatchVisibleSelectedVisitor( const Functor& functor ) : m_functor( functor ){
}
void visit( scene::Instance& instance ) const {
	PatchInstance* patch = Instance_getPatch( instance );
	if ( patch != 0
		 && instance.path().top().get().visible() ) {
		m_functor( *patch );
	}
}
};

template<typename Functor>
inline void Scene_forEachVisibleSelectedPatchInstance( const Functor& functor ){
	GlobalSelectionSystem().foreachSelected( PatchVisibleSelectedVisitor<Functor>( functor ) );
}

template<typename Functor>
class PatchForEachWalker : public scene::Graph::Walker
{
const Functor& m_functor;
public:
PatchForEachWalker( const Functor& functor ) : m_functor( functor ){
}
bool pre( const scene::Path& path, scene::Instance& instance ) const {
	if ( path.top().get().visible() ) {
		Patch* patch = Node_getPatch( path.top() );
		if ( patch != 0 ) {
			m_functor( *patch );
		}
	}
	else{
		return false;
	}
	return true;
}
};

template<typename Functor>
inline void Scene_forEachVisiblePatch( const Functor& functor ){
	GlobalSceneGraph().traverse( PatchForEachWalker<Functor>( functor ) );
}

template<typename Functor>
class PatchForEachSelectedWalker : public scene::Graph::Walker
{
const Functor& m_functor;
public:
PatchForEachSelectedWalker( const Functor& functor ) : m_functor( functor ){
}
bool pre( const scene::Path& path, scene::Instance& instance ) const {
	if ( path.top().get().visible() ) {
		Patch* patch = Node_getPatch( path.top() );
		if ( patch != 0
			 && Instance_getSelectable( instance )->isSelected() ) {
			m_functor( *patch );
		}
	}
	else{
		return false;
	}
	return true;
}
};

template<typename Functor>
inline void Scene_forEachVisibleSelectedPatch( const Functor& functor ){
	GlobalSceneGraph().traverse( PatchForEachSelectedWalker<Functor>( functor ) );
}

template<typename Functor>
class PatchForEachInstanceWalker : public scene::Graph::Walker
{
const Functor& m_functor;
public:
PatchForEachInstanceWalker( const Functor& functor ) : m_functor( functor ){
}
bool pre( const scene::Path& path, scene::Instance& instance ) const {
	if ( path.top().get().visible() ) {
		PatchInstance* patch = Instance_getPatch( instance );
		if ( patch != 0 ) {
			m_functor( *patch );
		}
	}
	else{
		return false;
	}
	return true;
}
};

template<typename Functor>
inline void Scene_forEachVisiblePatchInstance( const Functor& functor ){
	GlobalSceneGraph().traverse( PatchForEachInstanceWalker<Functor>( functor ) );
}

#endif
