/*
   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_SELECTABLE_H )
#define INCLUDED_SELECTABLE_H

#include <cstddef>

#include "generic/vector.h"
#include "scenelib.h"
#include "generic/callback.h"

class SelectionIntersection
{
float m_depth;
float m_distance;
public:
SelectionIntersection() : m_depth( 1 ), m_distance( 2 ){
}
SelectionIntersection( float depth, float distance ) : m_depth( depth ), m_distance( distance ){
}
bool operator<( const SelectionIntersection& other ) const {
	if ( m_distance != other.m_distance ) {
		return m_distance < other.m_distance;
	}
	if ( m_depth != other.m_depth ) {
		return m_depth < other.m_depth;
	}
	return false;
}
bool equalEpsilon( const SelectionIntersection& other, float distanceEpsilon, float depthEpsilon ) const {
	return float_equal_epsilon( m_distance, other.m_distance, distanceEpsilon )
		   && float_equal_epsilon( m_depth, other.m_depth, depthEpsilon );
}
float depth() const {
	return m_depth;
}
bool valid() const {
	return depth() < 1;
}
};

// returns true if self is closer than other
inline bool SelectionIntersection_closer( const SelectionIntersection& self, const SelectionIntersection& other ){
	return self < other;
}

// assigns other to best if other is closer than best
inline void assign_if_closer( SelectionIntersection& best, const SelectionIntersection& other ){
	if ( SelectionIntersection_closer( other, best ) ) {
		best = other;
	}
}




class VertexPointer
{
typedef const unsigned char* byte_pointer;
public:
typedef float elem_type;
typedef const elem_type* pointer;
typedef const elem_type& reference;

class iterator
{
public:
iterator() {}
iterator( byte_pointer vertices, std::size_t stride )
	: m_iter( vertices ), m_stride( stride ) {}

bool operator==( const iterator& other ) const {
	return m_iter == other.m_iter;
}
bool operator!=( const iterator& other ) const {
	return !operator==( other );
}

iterator operator+( std::size_t i ){
	return iterator( m_iter + i * m_stride, m_stride );
}
iterator operator+=( std::size_t i ){
	m_iter += i * m_stride;
	return *this;
}
iterator& operator++(){
	m_iter += m_stride;
	return *this;
}
iterator operator++( int ){
	iterator tmp = *this;
	m_iter += m_stride;
	return tmp;
}
reference operator*() const {
	return *reinterpret_cast<pointer>( m_iter );
}
private:
byte_pointer m_iter;
std::size_t m_stride;
};

VertexPointer( pointer vertices, std::size_t stride )
	: m_vertices( reinterpret_cast<byte_pointer>( vertices ) ), m_stride( stride ) {}

iterator begin() const {
	return iterator( m_vertices, m_stride );
}

reference operator[]( std::size_t i ) const {
	return *reinterpret_cast<pointer>( m_vertices + m_stride * i );
}

private:
byte_pointer m_vertices;
std::size_t m_stride;
};

class IndexPointer
{
public:
typedef unsigned int index_type;
typedef const index_type* pointer;

class iterator
{
public:
iterator( pointer iter ) : m_iter( iter ) {}

bool operator==( const iterator& other ) const {
	return m_iter == other.m_iter;
}
bool operator!=( const iterator& other ) const {
	return !operator==( other );
}

iterator operator+( std::size_t i ){
	return m_iter + i;
}
iterator operator+=( std::size_t i ){
	return m_iter += i;
}
iterator operator++(){
	return ++m_iter;
}
iterator operator++( int ){
	return m_iter++;
}
const index_type& operator*() const {
	return *m_iter;
}
private:
void increment(){
	++m_iter;
}
pointer m_iter;
};

IndexPointer( pointer indices, std::size_t count )
	: m_indices( indices ), m_finish( indices + count ) {}

iterator begin() const {
	return m_indices;
}
iterator end() const {
	return m_finish;
}

private:
pointer m_indices;
pointer m_finish;
};

template<typename Element> class BasicVector3;
typedef BasicVector3<float> Vector3;
class Matrix4;
class VolumeTest;

class SelectionTest
{
public:
virtual void BeginMesh( const Matrix4& localToWorld, bool twoSided = false ) = 0;
virtual const VolumeTest& getVolume() const = 0;
virtual const Vector3& getNear() const = 0;
virtual const Vector3& getFar() const = 0;
virtual void TestPoint( const Vector3& point, SelectionIntersection& best ) = 0;
virtual void TestPolygon( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ) = 0;
virtual void TestLineLoop( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ) = 0;
virtual void TestLineStrip( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ) = 0;
virtual void TestLines( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ) = 0;
virtual void TestTriangles( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ) = 0;
virtual void TestQuads( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ) = 0;
virtual void TestQuadStrip( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ) = 0;
};

class Selectable;

class Selector
{
public:
virtual void pushSelectable( Selectable& selectable ) = 0;
virtual void popSelectable() = 0;
virtual void addIntersection( const SelectionIntersection& intersection ) = 0;
};

inline void Selector_add( Selector& selector, Selectable& selectable ){
	selector.pushSelectable( selectable );
	selector.addIntersection( SelectionIntersection( 0, 0 ) );
	selector.popSelectable();
}

inline void Selector_add( Selector& selector, Selectable& selectable, const SelectionIntersection& intersection ){
	selector.pushSelectable( selectable );
	selector.addIntersection( intersection );
	selector.popSelectable();
}


class VolumeTest;
class SelectionTestable
{
public:
STRING_CONSTANT( Name, "SelectionTestable" );

virtual void testSelect( Selector& selector, SelectionTest& test ) = 0;
};

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


class Plane3;
typedef Callback<void(const Plane3&)> PlaneCallback;

class SelectedPlanes
{
public:
virtual bool contains( const Plane3& plane ) const = 0;
};

class PlaneSelectable
{
public:
STRING_CONSTANT( Name, "PlaneSelectable" );

virtual void selectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback ) = 0;
virtual void selectReversedPlanes( Selector& selector, const SelectedPlanes& selectedPlanes ) = 0;
};



#endif
