/*
* This source file is part of the osgOcean library
* 
* Copyright (C) 2009 Kim Bale
* Copyright (C) 2009 The University of Hull, UK
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.

* This program 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 Lesser General Public License for more details.
* http://www.gnu.org/copyleft/lesser.txt.
*/

#pragma once
#include <osgOcean/Export>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/BlendFunc>
#include <osg/Texture2D>
#include <osg/Version>
#include <osgDB/ReadFile>
#include <osgUtil/CullVisitor>
#include <string>
#include <osgOcean/WaterTrochoids>

namespace osgOcean
{
	/** 
	* Creates and animates God Ray geometry.
	*/
	class OSGOCEAN_EXPORT GodRays : public osg::Geode
	{
	private:
		bool           _isDirty;         /**< Rebuild flag. */
		bool           _isStateDirty;    /**< Rebuild stateset flag. */
		unsigned int   _numOfRays;       /**< Number of ray parallepipeds (_numOfRays*_numOfRays). */
		WaterTrochoids _trochoids;       /**< Generates and packs the trochoid variables required in the vertex shader. */
		osg::Vec3f     _sunDirection;    /**< Direction of the sun. */
		osg::Vec3f     _extinction;      /**< Extinction coeffecient (RGB) Controls the dispersion of light along the the length of the God Ray */
		float          _baseWaterHeight; /**< Height of the ocean surface */

		osg::ref_ptr<osg::StateSet>   _stateSet;
		osg::ref_ptr<osg::FloatArray> _constants; /**< Stores the trochoid variables. */

	public:
		GodRays(void);
		GodRays(unsigned int numOfRays, const osg::Vec3f& sunDir, float baseWaterHeight);
		GodRays(const GodRays& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY );

		virtual const char* libraryName() const { return "osgOcean"; }
		virtual const char* className() const { return "GodRays"; }
		virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const GodRays*>(obj) != 0; }

	protected:
		~GodRays(void){};

	public:
		/** 
		* Create and add a grid of vertices for vertex shader displacement, and add the glare quad geometry. 
		*/
		void build(void);

		/** 
		* Creates God Rays stateset.
		* Loads shaders and set uniforms.
		* Sets up trochoid generation.
		*/
		void buildStateSet(void);
		
		/**
		* Sets the number rays along one axis of the grid.
		* Dirties the geometry.
		*/
		inline void setNumOfRays( unsigned int num ){
			_numOfRays = num;
			_isDirty = true;
		}

		inline void setBaseWaterLevel(float height){
			_baseWaterHeight = height;
		}

		/** 
		* Sets the sun direction.
		* If stateset is valid it will also update the uniform.
		*/
		inline void setSunDirection(const osg::Vec3f& dir)
		{
			_sunDirection = dir;

			if( _stateSet.valid() )
				_stateSet->getUniform("uSunDir")->set(dir);
		}

		/** 
		* Set the extinction coefficient (RGB).
		* If stateset is valid it will also update the uniform.
		*/
		inline void setExtinctionCoeff( const osg::Vec3f& coeff )
		{
			_extinction = coeff;

			if( _stateSet.valid() )
				_stateSet->getUniform("uExtinction_c")->set(coeff);
		}

	private:
		/** 
		* Create the geometry and textures for the glare quad and attaches shaders.
		*/
		osg::Geometry* createGlareQuad(void);

		/** 
		* Create the parallepiped geometry for the rays and attaches shaders.
		*/
		osg::Geometry* createRayShafts(void);

		/** 
		* Updates shader uniforms and updates trochoids.
		* Builds geometry/stateset if dirty flags are set.
		*/
		void update(float time, const osg::Vec3f& eye, const double& fov );

		inline int idx( int c, int r, int rowLen )
		{
			return c+r*rowLen;
		}

		/** 
		* Loads and returns god ray shaders.
		*/
		osg::Program* createGodRayProgram(void);

		/** 
		* Loads and returns god ray glare shaders.
		*/
		osg::Program* createGodRayGlareProgram(void);

		/** 
		* Computes the refracted ray.
		* @param I Incident ray.
		* @param N Surface normal.
		* &param ration Refractive index ratio (1/1.333 for water).
		*/
		osg::Vec3f refract( const float ratio, const osg::Vec3f& I, const osg::Vec3f& N );

	// ---------------------------------------------
	//            Callback declarations
	// ---------------------------------------------

	protected:
		/** 
		* Datatype for storing values from the cull visitor.
		*/
		class GodRayDataType: public osg::Referenced
		{
		private:
			GodRays& _godRays;
			osg::Vec3f _eye;
			double _fov;

		public:
			GodRayDataType( GodRays& godRays );
			
			GodRayDataType( const GodRayDataType& copy, 
				const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY );

			inline void setEye( const osg::Vec3f& eye ){
				_eye = eye;
			}

			inline void setFOV( const double& fov ){ 
				_fov = fov;
			}

			inline const osg::Vec3f& getEye( void ) const{
				return _eye;
			}

			void update( float time );
		};

		/** 
		* Update/Cull animation callback.
		* Cull callback stores the eye position and the field of view.
		* Update callback calls GodRays::update().
		*/
		class GodRayAnimationCallback: public osg::NodeCallback
		{
		public:
			virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
		};

		/** 
		* Custom compute bound callback.
		* Needed as translations are done within the vertex shader.
		* Moves the bounding box along with the eye's (x,y) position.
		*/
		class ComputeBoundsCallback: public osg::Drawable::ComputeBoundingBoxCallback
		{
		private:
			GodRays& _rays;
		public:
			ComputeBoundsCallback( GodRays& rays );

#if OSG_VERSION_LESS_THAN(3,3,2)
                        virtual osg::BoundingBox computeBound(const osg::Drawable&) const;
#else
			virtual osg::BoundingBox computeBoundingBox(const osg::Drawable&) const;
#endif
		};
	};
}
