/*
--------------------------------------------------------------------------------
This source file is part of SkyX.
Visit ---

Copyright (C) 2009 Xavier Verguín González <xavierverguin@hotmail.com>
                                           <xavyiy@gmail.com>

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 2 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.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.
--------------------------------------------------------------------------------
*/

#ifndef _SkyX_VClouds_GeometryBlock_H_
#define _SkyX_VClouds_GeometryBlock_H_

#include "SkyxPrerequisites.h"

namespace SkyX
{ 
	namespace VClouds
	{

	/*!
	*/
	class SkyXDllExport GeometryBlock
	{
	public:
		/*!
		Vertex struct
		*/
		struct VERTEX
		{
			float x,y,z,		//!< Position
				  xc, yc, zc,	//!< 3D Coords
				  u, v,			//!< Noise coords
				  o;			//!< Opacity
		};
	protected:
	private:
		bool mCreated;				//!< Has been create() already called?
        Ogre::MeshPtr mMesh;		//!< Ogre::MeshPtr
        Ogre::SubMesh* mSubMesh;	//!< Ogre::Submesh pointer
        Ogre::Entity* mEntity;		//!< Ogre::Entity pointer
        Ogre::HardwareVertexBufferSharedPtr mVertexBuffer;	//!< Vertex buffer
        Ogre::HardwareIndexBufferSharedPtr  mIndexBuffer;	//!< Index buffer
		VERTEX* mVertices;			//!< Vertices pointer
		int mNumberOfTriangles;		//!< Current number of triangles
		int mVertexCount;			//!< Vertex count
		float mHeight;				//!< Height
		Ogre::Radian mAlpha;		//!< Alpha angle 
		Ogre::Radian mBeta;			//!< Beta  angle 
		float mRadius;				//!< Radius
		Ogre::Radian mPhi;			//!< Acimutal angle
		int mNa, mNb, mNc;			//!< Number of slices per geometry zone
		float mA, mB, mC;			//!< A, B and C radius
		int mPosition;				//!< Number of block(Position)
		Ogre::Vector2 mV2Cos;		//!< Precomputed Cos vectors
		Ogre::Vector2 mV2Sin;		//!< Precomputed Sin vectors
		float mAlphaSin;			//!< PI - Alpha Sin
		float mBetaSin;				//!< PI - Beta
		Ogre::Vector3 mDisplacement;//!< Displacement
		Ogre::Vector2 mWorldOffset;	//!< World coords offset
		VClouds* mVClouds;			//!< VClouds pointer

	public:
		/*!
		Constructor
		@param vc VClouds pointer
		@param Height Field height (in woorld coordinates)
		@param Alpha Alpha angle
		@param Beta Beta angle
		@param Radius Total radius
		@param Phi Actimutal angle
		@param Na Number of slices in A zone
		@param Nb Number of slices in B zone
		@param Nc Number of slices in C zone
		*/
		GeometryBlock(VClouds* vc,
					  const float& Height, const Ogre::Radian& Alpha, const Ogre::Radian& Beta, 
					  const float& Radius, const Ogre::Radian& Phi, const int& Na, 
					  const int& Nb, const int& Nc, const int& Position);

		/*!
		Destructor
		*/
		~GeometryBlock();

		/*!
		Create
		*/
		void create();

		/*!
		Remove
		*/
		void remove();

		/*!
		Call every frame
		@param offset Amount of world units moved
        */
        void update(const Ogre::Real& offset);

		/*!
		Has been create() already called?
		@return true if created() have been already called, false if not
		*/
		inline const bool& isCreated() const
		{
			return mCreated;
		}

		/*!
		Get mesh
        @return Mesh
        */
        inline Ogre::MeshPtr getMesh()
        {
            return mMesh;
        }

        /*!
		Get sub mesh
        @return Sub mesh
        */
        inline Ogre::SubMesh* getSubMesh()
        {
            return mSubMesh;
        }

        /*!
		Get entity
        @return Entity
        */
        inline Ogre::Entity* getEntity()
        {
            return mEntity;
        }

		/*!
		Get hardware vertex buffer reference
        @return Ogre::HardwareVertexBufferSharedPtr reference
        */
        inline Ogre::HardwareVertexBufferSharedPtr &getHardwareVertexBuffer()
        {
            return mVertexBuffer;
        }

		/*!
		Get hardware index buffer reference
		@return Ogre::HardwareIndexBufferSharedPtr reference
		*/
		inline Ogre::HardwareIndexBufferSharedPtr &getHardwareIndexBuffer()
		{
			return mIndexBuffer;
		}

		/*!
		Set world offset
		@param WorldOffset World offset
		*/
		inline void setWorldOffset(const Ogre::Vector2& WorldOffset)
		{
			mWorldOffset = WorldOffset;
		}

		/*!
		Is the geometry block inside the camera frustum?
		@param c Camera
		@return true if yes, false if not
		*/
		const bool isInFrustum(Ogre::Camera* c) const;
	protected:
	private:
		/*!
		Build axis aligned box
		*/
		const Ogre::AxisAlignedBox _buildAABox() const;

		/*!
		Calculate data size
		*/
		void _calculateDataSize();

		/*!
		Create geometry
		*/
		void _createGeometry();

		/*!
		Update geometry
		*/
		void _updateGeometry();

		/*!
		Update zone C slice
		@param n Number of slice
		*/
		void _updateZoneCSlice(const int& n);
		
		/*!
		Update zone B slice
		@param n Number of slice
		*/
		void _updateZoneBSlice(const int& n);

		/*!
		Update zone A slice
		@param n Number of slice
		*/
		void _updateZoneASlice(const int& n);

		/*!
		Set vertex data
		@param index Vertex index
		@param o Slice opacity
		@param p Position
		*/
		void _setVertexData(const int& index, const Ogre::Vector3& p, const float& o);
	};

	}
}

#endif
