/*
--------------------------------------------------------------------------------
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_SkyX_H_
#define _SkyX_SkyX_H_

#include "SkyxPrerequisites.h"
#include "SkyxAstronomicalModel.h"
#include "SkyxMeshManager.h"
#include "SkyxAtmosphereManager.h"
#include "SkyxGPUManager.h"
#include "SkyxMoonManager.h"
#include "SkyxCloudsManager.h"
#include "SkyxVCloudsManager.h"
#include "VClouds/SkyxVClouds.h"
#include "SkyxColorGradient.h"

namespace SkyX
{

  #define _def_MaxFarClipDistance 99999.0f

	/*!
	Main SkyX class
	Create simple and beautiful skies!
  */
  class SkyXDllExport SkyX : public Ogre::Viewport::Listener,
                             public Ogre::RenderTargetListener
	{
	public:
		/*!
		Lighting mode enumeration
		SkyX is designed for true HDR rendering, but there're a lot of applications
		that doesn't use HDR rendering, due to this a little exponential tone-mapping 
		algoritm is applied to SkyX materials if LM_LDR is selected. (See: AtmosphereManager::Options::Exposure)
		Select LM_HDR if your app is designed for true HDR rendering.
		*/
		enum LightingMode
		{
			LM_LDR = 0,	//!< Low dynamic range
			LM_HDR = 1 	//!< High dynamic range
		};
	protected:
	private:
    MeshManager* mMeshManager;				       //!< Mesh manager
    AtmosphereManager* mAtmosphereManager;   //!< Atmosphere manager
    GPUManager* mGPUManager;				         //!< GPU manager
    MoonManager* mMoonManager;				       //!< Moon manager
    CloudsManager* mCloudsManager;			     //!< Clouds manager
    VCloudsManager* mVCloudsManager;		     //!< Volumetric clouds manager
    Ogre::SceneManager* mSceneManager;		   //!< Pointer to Ogre::SceneManager
    LightingMode mLightingMode;				       //! Lighting mode
    bool mStarfield;						             //!< Enable starfield?
    Ogre::Real mTimeMultiplier;				       //!< Time multiplier
    Ogre::Real mTimeOffset;					         //!< Time offset
    Ogre::String mResourceGroup;			       //!< Ogre resource group where to get skyX resources, "SkyX" as a default.
    AstronomicalModel* astronomicalModel;    //!< Sun & moon light direction provider.
    bool internalAstronomicalModel;          //!< Do we use SkyX basic sun & moon position model, or an external one was provided?
    typedef std::set<Ogre::Viewport*> ViewportList;
    ViewportList mViewportList;              //!< Viewports on which we want to render the sky.
    typedef std::map<Ogre::RenderTarget*, int> RenderTargetList;
    RenderTargetList mRenderTargetList;

	public:
		/*!
		Contructor 
		@param sm Ogre Scene manager pointer
    @param resourceGroup SkyX resources group name
    @param overrideAstronomicalModel override the default sun & moon light direction calculator (to provide more real data for example).
		*/
		SkyX(Ogre::SceneManager* sm, Ogre::String resourceGroup="SkyX", AstronomicalModel* overrideAstronomicalModel=0);

		/*!
		Destructor 
		*/
		~SkyX();

    /*!
    */
    void registerViewport(Ogre::Viewport* newViewport);

    /*!
    */
    void unregisterViewport(Ogre::Viewport* existingViewport);

    /*!
    */
    void unregisterAllViewports();

    /*!
		Call every frame
		@param timeSinceLastFrame Time elapsed since last frame
    */
    void update(const Ogre::Real &timeSinceLastFrame);

		/*!
		Set time multiplier
		@param TimeMultiplier Time multiplier
		@remarks The time multiplier can be a negative number, 0 will disable auto-updating
		         For setting a custom time of day, check: AtmosphereManager::Options::Time
		*/
		inline void setTimeMultiplier(const Ogre::Real& TimeMultiplier)
		{
			mTimeMultiplier = TimeMultiplier;
			mVCloudsManager->_updateWindSpeedConfig();
		}

		/*!
		Get time multiplier
		@return Time multiplier
		*/
		inline const Ogre::Real& getTimeMultiplier() const
		{
			return mTimeMultiplier;
		}

		/*!
		Get time offset
		@return Time offset
		@remarks Only for internal use
		*/
		inline const Ogre::Real& _getTimeOffset() const
		{
			return mTimeOffset;
		}

		/*!
		Get mesh manager
		@return Mesh manager pointer
		*/
		inline MeshManager* getMeshManager()
		{
			return mMeshManager;
		}

		/*!
		Get atmosphere manager
		@return Atmosphere manager pointer
		*/
		inline AtmosphereManager* getAtmosphereManager()
		{
			return mAtmosphereManager;
		}

		/*!
		Get GPU manager
		@return Atmosphere manager pointer
		*/
		inline GPUManager* getGPUManager()
		{
			return mGPUManager;
		}

		/*!
		Get moon manager
		@return Moon manager
		*/
		inline MoonManager* getMoonManager()
		{
			return mMoonManager;
		}

		/*!
		Get clouds manager
		@return Clouds manager
		*/
		inline CloudsManager* getCloudsManager()
		{
			return mCloudsManager;
		}

		/*!
		Get volumetric clouds manager
		@return Volumetric clouds manager
		*/
		inline VCloudsManager* getVCloudsManager()
		{
			return mVCloudsManager;
		}

    /*!
		Get astronomical model in use
		@return Astronomical model
		*/
    inline AstronomicalModel* getAstronomicalModel()
    {
      return astronomicalModel;
    }

		/*!
		Set lighting mode
		@param lm Lighting mode
		@remarks SkyX is designed for true HDR rendering, but there're a lot of applications
				 that doesn't use HDR rendering, due to this a little exponential tone-mapping 
				 algoritm is applied to SkyX materials if LM_LDR is selected. (See: AtmosphereManager::Options::Exposure)
				 Select LM_HDR if your app is designed for true HDR rendering.
		 */
		void setLightingMode(const LightingMode& lm);

		/*!
		Get lighting mode
		@return Lighting mode
		*/
		inline const LightingMode& getLightingMode() const
		{
			return mLightingMode;
		}

		/*!
		Set the starfield enabled/disabled
		@param Enabled true for starfield, false for not
		*/
		void setStarfieldEnabled(const bool& Enabled);

		/*!
		Is the starfield enable?
		@return true if the starfield is enabled, false if it isn't
		*/
		inline const bool& isStarfieldEnabled() const
		{
			return mStarfield;
		}

		/*!
		Get scene manager
		@return Ogre::SceneManager pointer
		*/
		inline Ogre::SceneManager* getSceneManager()
		{
			return mSceneManager;
		}

		/*!
		Get the name of skyX's resource group 
		*/
		inline Ogre::String getResourceGroup()
		{
      return mResourceGroup;
		}

    /*!
    @copydoc Ogre::Viewport::Listener::viewportDestroyed
    */
    virtual void viewportDestroyed(Ogre::Viewport* viewport);

    /*!
    @copydoc Ogre::RenderTargetListener::preViewportUpdate
    */
    virtual void preViewportUpdate(const Ogre::RenderTargetViewportEvent& evt);
	protected:
	private:
    /*!
    Update all components that needs the camera.
    */
    void _notifyCameraChanged(Ogre::Camera* newCamera);
	};

}

#endif

