#ifndef __SHADER_H__
#define __SHADER_H__

#include <stdarg.h>
#include <vector>
#include <map>
#include <string>

#include "refcount.h"
#include "type.h"
#include "interpolator.h"
#include "transform.h"

#include "public.h"

namespace xrt
{
	class ParameterSet;
	class Parameter;
	class ShaderEnvironment;
	class Shape;

	class PUBLIC Shader: public ReferenceCounted
	{
	public:
		struct ParameterInformation
		{
			const char *name; // name
			xrt::Type type; // data type
			std::vector<float> floats; // default float values
			std::vector<ustring> strings; // space name for matrices and
			// triples, for each array elem or default string values
		};

		struct ConnectionInfo
		{
			xrt::Shader* shader;
			std::string name;
			Type type;
			unsigned int id;
			int item;
			void *ptr;
		};

		class Connection
		{
		public:
			virtual void execute( ShaderEnvironment& ) = 0;
		};

		class PUBLIC ArgumentConnectionBase: public Connection
		{
		public:
			//struct
			//{
				xrt::Type type; // data type
				std::vector<float> floats; // default float values
				std::vector<ustring> strings; // space name for matrices and
				// triples, for each array elem or default string values
			//};
			ArgumentConnectionBase( xrt::Type type ): type( type ) {}
			virtual void createInterpolators( std::map< ustring, Transform >& ) = 0;
			virtual void deleteInterpolators() = 0;
			//void setValue( const float * );
			//void setValue( const char ** );
			//void setValue( const char *, const float * );
			//void setValue( const char **, const float * );
		};
		
		class FloatArrayConnection: public Connection
		{
		public:
			FloatArrayConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			float *src;
			float *dst;
			unsigned int length;
		};

		class TupleArrayConnection: public Connection
		{
		public:
			TupleArrayConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			Tuple *src;
			Tuple *dst;
			unsigned int length;
		};

		class StringArrayConnection: public Connection
		{
		public:
			StringArrayConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			const char **src;
			const char **dst;
			unsigned int length;
		};

		struct FloatData
		{
			FloatData( const ConnectionInfo& );
			void resolveAddress( ShaderEnvironment& );
			unsigned int id;
			int item;
			float *ptr;
		};

		struct TupleData
		{
			TupleData( const ConnectionInfo& );
			void resolveAddress( ShaderEnvironment& );
			unsigned int id;
			int item;
			Tuple *ptr;
		};

		class FloatConnection: public Connection
		{
		public:
			FloatConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			FloatData src, dst;
		};
			
		class TupleConnection: public Connection
		{
		public:
			TupleConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			TupleData src, dst;
		};

		class StringConnection: public Connection
		{
		public:
			StringConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			const char *src;
			const char *dst;
		};

		class MatrixConnection: public Connection
		{
		public:
			MatrixConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			Matrix *src;
			Matrix *dst;
		};

		class FloatTupleConnection: public Connection
		{
		public:
			FloatTupleConnection( const ConnectionInfo&, const ConnectionInfo& );
			void execute( ShaderEnvironment& );
		private:
			FloatData src;
			TupleData dst;
		};

	protected:
		struct Information
		{
			const char *name;
			unsigned int usedShaderEnvironment;
			unsigned int type;
		};

	public:
		struct Argument
		{
			Argument() {}
			Argument( const char* name, xrt::Type type, void* address ):
				name( name ), type( type ), address( address ) {}
			const char *name;
			xrt::Type type;
			void *address;
		};

		template< class T > class ArgumentConnection: public ArgumentConnectionBase
		{
		public:
			ArgumentConnection( const Shader*, const Parameter*, const Argument* );
			~ArgumentConnection();
			void execute( ShaderEnvironment& );
			void createInterpolators( std::map< ustring, Transform >& );

		private:
			T *ptr_;
			std::vector< Interpolator< T > * > interpolators;
			void deleteInterpolators();
		};

		Shader();
		~Shader();
		virtual void evaluate( ShaderEnvironment& ) = 0;
		virtual void init( ShaderEnvironment& ) = 0;
		virtual const Information& getInformation() const = 0;
		virtual const Argument* getArguments() const = 0;
		virtual unsigned int getNumArguments() const = 0;
		void init( ShaderEnvironment&, const Shape* );
		const char* getLayerName() const { return layerName.c_str(); }
		void setLayerName( const char* layer ) { layerName = layer; }
		std::vector < Connection * >& connections() { return m_connections; }
		std::vector < ArgumentConnectionBase * >& arguments() { return m_arguments; }
		void setCoordinateSystem( Transform &transform ) { shaderToWorld = transform; }
		const Transform& getCoordinateSystem() const { return shaderToWorld; }
		const Argument* find( const char* ) const;
		const char * name() const { return getInformation().name; }
		unsigned int type() const { return getInformation().type; }
		unsigned int usedShaderEnvironment() const { return getInformation().usedShaderEnvironment; }
		bool getMessage( const char* name, float& variable );
	private:
		std::string layerName;
		std::vector < Connection* > m_connections;
		std::vector < ArgumentConnectionBase* > m_arguments;
		Transform shaderToWorld;
	};

	//template< class T > 
	//void Shader::ArgumentConnection<T>::execute( ShaderEnvironment& sv )
	//{
	//	T *ptr = ptr_;
	//	size_t count = interpolators.size();

	//	for ( unsigned int i = 0; i < count; i++ )
	//		*(ptr++) = interpolators[i]->at( sv.u, sv.v );
	//}

	template<> 
	EXPORT void Shader::ArgumentConnection<float>::createInterpolators( std::map< ustring, Transform >&  );
	template<> 
	EXPORT void Shader::ArgumentConnection<ustring>::createInterpolators( std::map< ustring, Transform >&  );
	template<> 
	EXPORT void Shader::ArgumentConnection<Color>::createInterpolators( std::map< ustring, Transform >&  );
	template<> 
	EXPORT void Shader::ArgumentConnection<Tuple>::createInterpolators( std::map< ustring, Transform >&  );
	template<> 
	EXPORT void Shader::ArgumentConnection<Matrix>::createInterpolators( std::map< ustring, Transform >&  );

	template< class T > 
	void Shader::ArgumentConnection< T >::deleteInterpolators()
	{
		unsigned int count = interpolators.size();

		for ( unsigned int i = 0; i < count; i++ )
			delete interpolators[i];

		interpolators.clear();
	}

	template< class T > 
	Shader::ArgumentConnection< T >::~ArgumentConnection()
	{
		deleteInterpolators();
	}
	//template< class T > 
	//void Shader::Parameter<T>::setValue( unsigned int i, Interpolator< T > *interpolator )
	//{
	//	interpolators_[i] = interpolator;
	//}


	//template< class T > 
	//Shader::Parameter<T>::~Parameter()
	//{
	//	clear();
	//}

	//template< class T > 
	//Shader::Parameter<T>::Parameter( Shader::Parameter<T> &p )
	//{
	//	for ( unsigned int i = 0; i < p.m_interpolators.size(); i++ )
	//		interpolators_.push_back( new Interpolator<T>( p.interpolators_[i] ) );
	//}

	EXPORT Shader *createShader( const std::string &, const std::string &, std::map< ustring, Transform > &, ParameterSet & );

};
#endif //__SHADER_H__
