#pragma once

#include "Mix/Geometry/Frustum.h"
#include "Mix/Scene/ICamera.h"
#include "Mix/Private/Scene/Common/RendererTypes.h"
#include "Mix/Private/Scene/Common/RendererObject.h"
#include "Mix/Timer.h"

namespace Mix{ namespace Scene{ namespace Common{

	class OctreeView;
	class Renderer;

	class Camera : public Mix::Scene::Common::RendererObject
	{
	public:
		enum PUBLIC_VALUE
		{
			LFT_MAX = 3, // レンズフレアを装飾するテクスチャの数
			FI_MAX	= 3, // フィルミックの最大数( NONE 含む )
		};

		struct INTERNAL_VISIBLITY_SETTINGS
		{
			Float32 loFalloffStart;
			Float32 loInvFalloffAmount;

			Float32 plFalloffStart;
			Float32 plFalloffMiddle;

			Float32 sdwFalloffStart;
			Float32 sdwInvFalloffAmount;
		};

		struct INTERNAL_AMBIENT_OCCLUSION_SETTINGS
		{
			Float32 radius;
			Float32 threshold;
			Float32 depth;
		};

		struct INTERNAL_SHADOW_MAPPING_SETTINGS
		{
			Float32 depthBias;
			Float32 pcfRange;
			Float32 pcfMul;
		};

		enum LUMINOSITY_ADAPTATION_TYPE
		{
			LUM_AD_SPECIFY	= 0,
			LUM_AD_FORCE	= 1,
			LUM_AD_SLOWLY	= 2,
		};

		struct INTERNAL_LIGHT_SHAFTS_SETTINGS
		{
			Float32 start;
			Float32 end;
			Float32 invDist;
		};

		struct INTERNAL_LENS_FLARE_SETTINGS
		{
			Mix::Vector3 distoVec;
			Mix::Matrix4x4 camMat;
		};

		struct INTERNAL_MOTION_BLUR_SETTINGS
		{
			//線形深度を指数深度へ変換するためのパラメータ
			Float32 cp0;	// 2.0f * nearZ
			Float32 cp1;	// farZ + nearZ
			Float32 cp2;	// 1.0f / -( farZ - nearZ )

			Float32 scale;	//scale * 0.5f
			Float32 intensity;

			Float32 invVolume;
			Float32 dist;
		};

		struct INTERNAL_CHROMATISM_SETTINGS
		{
			Float32 fiStartZ;
			Float32 invDist;
		};

		struct INTERNAL_DOF_SETTINGS
		{
			float nearZ;
			float farZ;
			float invNearDist;
			float invFarDist;
			float invBlurThresholdN;
			float invBlurThresholdF;
		};

		struct INTERNAL_VIGNETTE_SETTINGS
		{
			Float32 start;
			Float32 invDist;
		};

	private:
		enum PRIVATE_VALUE
		{
			//シャドウマッピング
			SMPS_MAX = 3,	//PCFサイズ列挙定数の最大数

			//フィルミック
			FI_SETTINGS_BUFF_SIZE = 32, //パラメータを格納するバッファのサイズ

			//モーションブラーの状態の保持数
			MB_MIN_AMOUNT	= 1,
			MB_MAX_AMOUNT	= 4,
		};

		//モーションブラー : 状態列挙定数
		enum MOTION_BLUR_STATE
		{
			MB_INIT		= 0,	// 初期化       → MB_WAIT
			MB_WAIT		= 1,	// 待機中       → MB_ACTIVE
			MB_ACTIVE	= 3,	// 活動中       → MB_IDLE
			MB_IDLE		= 4,	// アイドリング → MB_ACTIVE or MB_WAIT
		};

		struct MOTION_BLUR_CONTEXT
		{
			// SetMotionBlurSettings で更新される値
			Float32 invEchoTimeLength;

			// Update で更新される値

			Mix::Vector3 viewDir;
			Mix::Vector3 eyePos;

			Camera::MOTION_BLUR_STATE state;

			UInt32 preVPHead;
			UInt32 preVPTail;
			UInt32 preVPCount;
			Mix::Matrix4x4 preVPMats[Camera::MB_MAX_AMOUNT];

			Float32 preSpeed;
			Float32 step;
			Float32 pos;
		};

	private:
		Mix::Scene::Common::Renderer* m_pRenderer;
		Mix::Scene::Common::OctreeView* m_pOctView;

		Mix::Scene::CAMERA_CONFIG m_Config;
		UInt32 m_ModConfigCaps;

		Mix::Vector4 m_BGColor;

		Mix::Graphics::TEXTURE_FILTER_TYPE m_FilterType;

		Float32 m_FovY;
		Float32 m_DefAspect;
		Float32 m_Aspect;
		Float32 m_NearZ;
		Float32 m_FarZ;
		Mix::Matrix4x4 m_ProjMat;

		Mix::Geometry::Frustum m_Frustum;

		Mix::Vector3 m_ViewVec;
		Mix::Vector3 m_ViewForward;
		Mix::Vector3 m_ViewUpward;
		Mix::Vector3 m_ViewCrossDir;
		Mix::Matrix4x4 m_PreViewProjMat;
		Mix::Matrix4x4 m_InvViewProjMat;
		Mix::Matrix4x4 m_BillboardMat;
		Mix::Matrix4x4 m_BillboardMatY;

		Mix::Scene::ICamera::VISIBILITY_SETTINGS m_VisiSettings;
		Camera::INTERNAL_VISIBLITY_SETTINGS m_InternalVisiSettings;

		Boolean m_bAOEnabled;
		Mix::Scene::ICamera::AMBIENT_OCCLUSION_SETTINGS m_AOSettings;
		Camera::INTERNAL_AMBIENT_OCCLUSION_SETTINGS m_InternalAOSettings;
		Mix::Graphics::ITexture* m_pAOTex;

		Boolean m_bSMEnabled;
		Mix::Scene::ICamera::SHADOW_MAPPING_SETTINGS m_SMSettings;
		Camera::INTERNAL_SHADOW_MAPPING_SETTINGS m_InternalSMSettings;

		Mix::Scene::ICamera::LUMINOSITY_SETTINGS m_LumSettings;
		UInt32 m_LumAdSuspendCount;
		Camera::LUMINOSITY_ADAPTATION_TYPE m_LumAdType;
		Float32 m_LumAdValue;

		Boolean m_bBloomEnabled;
		UInt32 m_BloomOverflowNum;
		Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::ICamera::BLOOM_OVERFLOW> m_BloomOverflows;
		Mix::Scene::ICamera::BLOOM_SETTINGS m_BloomSettings;

		Boolean m_bLightShaftsEnabled;
		Mix::Scene::ICamera::LIGHT_SHAFTS_SETTINGS m_LightShaftsSettings;
		Camera::INTERNAL_LIGHT_SHAFTS_SETTINGS m_InternalLightShaftsSettings;
		Mix::Graphics::ITexture* m_pLightShaftsTex;

		Boolean m_bLensFlareEnabled;
		Mix::Scene::ICamera::LENS_FLARE_SETTINGS m_LensFlareSettings;
		Camera::INTERNAL_LENS_FLARE_SETTINGS m_InternalLensFlareSettings;
		Mix::Graphics::ITexture* m_pLensFlareTex[Camera::LFT_MAX];

		Boolean m_bFilmicEnabled;
		Mix::Scene::ICamera::FILMIC_TYPE m_FilmicType;
		UInt8 m_FilmicSettingsBuff[Camera::FI_SETTINGS_BUFF_SIZE];
		UInt32 m_FilmicSettingsSize;

		Boolean m_bGammaEnabled;
		Mix::Vector3 m_GammaValue;
		Mix::Vector3 m_InternalGammaValue;

		Boolean m_bMotBlurEnabled;
		Mix::Scene::ICamera::MOTION_BLUR_SETTINGS m_MotBlurSettings;
		Camera::INTERNAL_MOTION_BLUR_SETTINGS m_InternalMotBlurSettings;
		Camera::MOTION_BLUR_CONTEXT m_MotBlurCtx;

		Boolean m_bChromatismEnabled;
		Mix::Scene::ICamera::CHROMATISM_SETTINGS m_ChromatismSettings;
		Camera::INTERNAL_CHROMATISM_SETTINGS m_InternalChromatismSettings;

		Boolean m_bDofEnabled;
		Mix::Scene::ICamera::DOF_SETTINGS m_DofSettings;
		Camera::INTERNAL_DOF_SETTINGS m_InternalDofSettings;

		Boolean m_bLensDistEnabled;
		Mix::Scene::ICamera::DISTORTION_SETTINGS m_LensDistSettings;

		Boolean m_bVignetteEnabled;
		Mix::Scene::ICamera::VIGNETTE_SETTINGS m_VignetteSettings;
		Camera::INTERNAL_VIGNETTE_SETTINGS m_InternalVignetteSettings;

		Mix::Scene::ICamera::ANTIALIASING_TYPE m_AAType;

		Boolean m_bUnderWater;

#ifdef _DEBUG
		Mix::String m_DebugName;
		Mix::Graphics::FILL_TYPE m_DebFillType;
		Mix::Scene::DEBUG_DRAW_METHOD m_DebDrawMethod;
		UInt32 m_DebDrawFlags;
		Mix::Scene::ICamera::DEBUG_DRAW_FILIMIC_SETTINGS m_DebDrawFilmicSettings;
#endif //_DEBUG

	protected:
		Mix::Vector3 m_Eye;
		Mix::Vector3 m_At;
		Mix::Matrix4x4 m_ViewMat;

	public:
		Camera( const Mix::Scene::CAMERA_CONFIG& config );
		virtual ~Camera( void );

		Boolean Attach( Mix::Scene::Common::Renderer* pRenderer, Mix::Scene::Common::OctreeView* pOctView );
		void Detach( void );

		UInt32 GetID( void ) const;

		const Mix::Scene::CAMERA_CONFIG& GetConfig( void ) const;
		Boolean SetConfig( const Mix::Scene::CAMERA_CONFIG& config );
		void UpdateConfig( void );

		const Mix::Vector4& GetBackgroundColor( void ) const;
		void SetBackgroundColor( const Mix::Vector4& color );

		Mix::Graphics::TEXTURE_FILTER_TYPE GetFilterType( void ) const;
		void SetFilterType( Mix::Graphics::TEXTURE_FILTER_TYPE type );

		const Mix::Matrix4x4& GetProjectionMatrix( void ) const;
		void SetProjection( Float32 fovY, Float32 nearZ, Float32 farZ );
		void SetProjection( Float32 fovY, Float32 aspect, Float32 nearZ, Float32 farZ );
		void SetProjection( Float32 fovY, UInt32 width, UInt32 height, Float32 nearZ, Float32 farZ );
		Float32 GetFovY( void ) const;
		Float32 GetDefaultAspect( void ) const;
		Float32 GetAspect( void ) const;
		Float32 GetNearZ( void ) const;
		Float32 GetFarZ( void ) const;

		const Mix::Vector3& GetEye( void ) const;
		const Mix::Vector3& GetAt( void ) const;
		const Mix::Vector3& GetViewVector( void ) const;
		const Mix::Vector3& GetViewForward( void ) const;
		const Mix::Vector3& GetViewUpward( void ) const;
		const Mix::Vector3& GetViewCrossDirection( void ) const;
		const Mix::Matrix4x4& GetViewMatrix( void ) const;

		const Mix::Geometry::Frustum& GetFrustum( void ) const;

		const Mix::Matrix4x4& GetViewProjectionMatrix( void ) const;
		const Mix::Matrix4x4& GetInvViewProjectionMatrix( void ) const;

		const Mix::Matrix4x4& GetBillboardMatrix( void ) const;
		const Mix::Matrix4x4& GetBillboardMatrixY( void ) const;

		Mix::Point Project( const Mix::Vector3& pos ) const;
		Mix::Point Project( const Mix::Point& screenPos, const Mix::Vector3& pos ) const;
		Mix::Point Project( const Mix::Rectangle& screenRect, const Mix::Vector3& pos ) const;

		Mix::Vector3 Unproject( const Mix::Point& pos, Float32 z ) const;
		Mix::Vector3 Unproject( const Mix::Point& screenPos, const Mix::Point& pos, Float32 z ) const;
		Mix::Vector3 Unproject( const Mix::Rectangle& screenRect, const Mix::Point& pos, Float32 z ) const;

		Boolean DragObject( const Mix::Point& pos );
		Boolean DragObject( const Mix::Point& screenPos, const Mix::Point& pos );
		Boolean DragObject( const Mix::Rectangle& screenRect, const Mix::Point& pos );

		Boolean IsUnderWater( void ) const;
		void SetUnderWater( Boolean state );

		void Update( Float32 dt );
		Boolean Draw( void );

		const Mix::Scene::ICamera::VISIBILITY_SETTINGS& GetVisiblitySettings( void ) const;
		const Camera::INTERNAL_VISIBLITY_SETTINGS& GetInternalVisiblitySettings( void ) const;
		void SetVisiblitySettings( const Mix::Scene::ICamera::VISIBILITY_SETTINGS& settings );

		Boolean IsAmbientOcclusionAvailabled( void ) const;
		Boolean IsAmbientOcclusionEnabled( void ) const;
		void SetAmbientOcclusionEnabled( Boolean state );
		const Mix::Scene::ICamera::AMBIENT_OCCLUSION_SETTINGS& GetAmbientOcclusionSettings( void ) const;
		const Camera::INTERNAL_AMBIENT_OCCLUSION_SETTINGS& GetInternalAmbientOcclusionSettings( void ) const;
		void SetAmbientOcclusionSettings( const Mix::Scene::ICamera::AMBIENT_OCCLUSION_SETTINGS& settings );
		Boolean GetAmbientOcclusionTexture( Mix::Graphics::ITexture** ppTex );
		Mix::Graphics::ITexture* GetAmbientOcclusionTexturePtr( void  ) const;
		void SetAmbientOcclusionTexture( Mix::Graphics::ITexture* pTex );

		Boolean IsShadowMappingAvailabled( void ) const;
		Boolean IsShadowMappingEnabled( void ) const;
		void SetShadowMappingEnabled( Boolean state );
		const Mix::Scene::ICamera::SHADOW_MAPPING_SETTINGS& GetShadowMappingSettings( void ) const;
		const Camera::INTERNAL_SHADOW_MAPPING_SETTINGS& GetInternalShadowMappingSettings( void ) const;
		void SetShadowMappingSettings( const Mix::Scene::ICamera::SHADOW_MAPPING_SETTINGS& settings );

		Boolean IsLuminosityAvailabled( void ) const;
		const Mix::Scene::ICamera::LUMINOSITY_SETTINGS& GetLuminositySettings( void ) const;
		void SetLuminositySettings( const Mix::Scene::ICamera::LUMINOSITY_SETTINGS& settings );
		Boolean IsLuminosityAdaptationEnabled( void ) const;
		Camera::LUMINOSITY_ADAPTATION_TYPE GetLuminosityAdaptationType( void );
		Float32 GetLuminosityAdaptationValue( void ) const;
		UInt32 ResumeLuminosityAdaptation( void );
		UInt32 SusupendLuminosityAdaptation( void );
		UInt32 GetLuminosityAdaptationSuspendCount( void ) const;
		void ForceLuminosityAdaptation( void );
		void SetLuminosityAdaptation( Float32 lum );

		Boolean IsBloomAvailabled( void ) const;
		Boolean IsBloomEnabled( void ) const;
		void SetBloomEnabled( Boolean state );
		UInt32 GetBloomOverflowNum( void ) const;
		const Mix::Scene::ICamera::BLOOM_OVERFLOW& GetBloomOverflow( UInt32 index ) const;
		void SetBloomOverflow( UInt32 index, const Mix::Scene::ICamera::BLOOM_OVERFLOW& overflow );
		void ResizeBloomOverflows( UInt32 num );
		const Mix::Scene::ICamera::BLOOM_SETTINGS& GetBloomSettings( void ) const;
		void SetBloomSettings( const Mix::Scene::ICamera::BLOOM_SETTINGS& settings );

		Boolean IsLightShaftsAvailabled( void ) const;
		Boolean IsLightShaftsEnabled( void ) const;
		void SetLightShaftsEnabled( Boolean state );
		const Mix::Scene::ICamera::LIGHT_SHAFTS_SETTINGS& GetLightShaftsSettings( void ) const;
		void SetLightShaftsSettings( const Mix::Scene::ICamera::LIGHT_SHAFTS_SETTINGS& settings );
		Boolean GetLightShaftsTexture( Mix::Graphics::ITexture** ppTex );
		Mix::Graphics::ITexture* GetLightShaftsTexturePtr( void  ) const;
		void SetLightShaftsTexture( Mix::Graphics::ITexture* pTex );
		const Camera::INTERNAL_LIGHT_SHAFTS_SETTINGS& GetInternalLightShaftsSettings( void ) const;

		Boolean IsLensFlareAvailabled( void ) const;
		Boolean IsLensFlareEnabled( void ) const;
		void SetLensFlareEnabled( Boolean state );
		const Mix::Scene::ICamera::LENS_FLARE_SETTINGS& GetLensFlareSettings( void ) const;
		const Camera::INTERNAL_LENS_FLARE_SETTINGS& GetInternalLensFlareSettings( void ) const;
		void SetLensFlareSettings( const Mix::Scene::ICamera::LENS_FLARE_SETTINGS& settings );
		Boolean GetLensFlareTexture( Mix::Scene::ICamera::LENS_FLARE_TEXTURE_TYPE type, Mix::Graphics::ITexture** ppTexture );
		Mix::Graphics::ITexture* GetLensFlareTexturePtr( Mix::Scene::ICamera::LENS_FLARE_TEXTURE_TYPE type ) const;
		void SetLensFlareTexture( Mix::Scene::ICamera::LENS_FLARE_TEXTURE_TYPE type, Mix::Graphics::ITexture* pTexture );

		Boolean IsFilmicEnabled( void ) const;
		void SetFilmicEnabled( Boolean state );
		Mix::Scene::ICamera::FILMIC_TYPE GetFilmicType( void ) const;
		Boolean GetFilmicSettings( void* pSettings, UInt32 size ) const;
		const void* GetFilmicSettingsBuff( UInt32 settingsSize ) const;
		Boolean SetFilmicType( Mix::Scene::ICamera::FILMIC_TYPE type, const void* pSettings, UInt32 size );

		Boolean IsGammaEnabled( void ) const;
		void SetGammaEnabled( Boolean state );
		const Mix::Vector3& GetGammaValue( void ) const;
		const Mix::Vector3& GetInternalGammaValue( void ) const;
		void SetGammaValue( const Mix::Vector3& value );
		void SetGammaValue( Float32 value );

		Boolean IsMotionBlurEnabled( void ) const;
		void SetMotionBlurEnabled( Boolean state );
		const Mix::Scene::ICamera::MOTION_BLUR_SETTINGS& GetMotionBlurSettings( void ) const;
		void SetMotionBlurSettings( const Mix::Scene::ICamera::MOTION_BLUR_SETTINGS& settings );
		const Camera::INTERNAL_MOTION_BLUR_SETTINGS& GetInternalMotionBlurSettings( void ) const;
		Boolean IsMotionBlurActivated( void ) const;
		UInt32 GetMotionBlurCurrentAmount( void ) const;
		const Mix::Matrix4x4& GetMotionBlurPreVPMat( void ) const;
		const Mix::Matrix4x4& GetMotionBlurPreVPMatByIndex( UInt32 index ) const;

		Boolean IsChromatismAvailabled( void ) const;
		Boolean IsChromatismEnabled( void ) const;
		void SetChromatismEnabled( Boolean state );
		const Mix::Scene::ICamera::CHROMATISM_SETTINGS& GetChromatismSettings( void ) const;
		const Camera::INTERNAL_CHROMATISM_SETTINGS& GetInternalChromatismSettings( void ) const;
		void SetChromatismSettings( const Mix::Scene::ICamera::CHROMATISM_SETTINGS& settings );

		Boolean IsDofAvailabled( void ) const;
		Boolean IsDofEnabled( void ) const;
		void SetDofEnabled( Boolean state );
		const Mix::Scene::ICamera::DOF_SETTINGS& GetDofSettings( void ) const;
		const Camera::INTERNAL_DOF_SETTINGS& GetInternalDofSettings( void ) const;
		void SetDofSettings( const Mix::Scene::ICamera::DOF_SETTINGS& settings );

		Boolean IsLensDistortionEnabled( void ) const;
		void SetLensDistortionEnabled( Boolean state );
		const Mix::Scene::ICamera::DISTORTION_SETTINGS& GetLendDistortionSettings( void ) const;
		void SetLendDistortionSettings( const Mix::Scene::ICamera::DISTORTION_SETTINGS& settings );

		Boolean IsVignetteEnabled( void ) const;
		void SetVignetteEnabled( Boolean state );
		const Mix::Scene::ICamera::VIGNETTE_SETTINGS& GetVignetteSettings( void ) const;
		const Camera::INTERNAL_VIGNETTE_SETTINGS& GetInternalVignetteSettings( void ) const;
		void SetVignetteSettings( const Mix::Scene::ICamera::VIGNETTE_SETTINGS& settings );

		Mix::Scene::ICamera::ANTIALIASING_TYPE GetAntiAliasingType( void ) const;
		void SetAntiAliasingType( Mix::Scene::ICamera::ANTIALIASING_TYPE type );

		void Debug_SetFillType( Mix::Graphics::FILL_TYPE type );
		Mix::Graphics::FILL_TYPE Debug_GetFillType( void ) const;

		void Debug_SetDrawMethod( Mix::Scene::DEBUG_DRAW_METHOD method );
		Mix::Scene::DEBUG_DRAW_METHOD Debug_GetDrawMethod( void ) const;

		void Debug_SetDrawFlags( UInt32 flags );
		UInt32 Debug_GetDrawFlags( void ) const;

		const Mix::Scene::ICamera::DEBUG_DRAW_FILIMIC_SETTINGS& Debug_GetDrawFilmicSettings( void ) const;
		void Debug_SetDrawFilmicSettings( const Mix::Scene::ICamera::DEBUG_DRAW_FILIMIC_SETTINGS& settings );

		UInt32 Debug_GetProfile( UInt32 type, void* pProf, UInt32 size ) const;

		UInt32 Debug_GetImageNum( UInt32 type ) const;
		Boolean Debug_GetImage( UInt32 type, UInt32 index, Mix::Graphics::ITexture** ppTexture );

#ifdef _DEBUG
		const wchar_t* Debug_GetName( void ) const;
		void Debug_SetName( const wchar_t* pName );
#endif //_DEBUG

	private:
		static Float32 GetMotionBlurSpeed( Float32 minValue, Float32 maxValue, Float32 curCalue );

	private:
		static const Float32 MIN_FALLOFF_AMOUNT;
		static const UInt32 SM_PCF_SIZE_TABLE[Camera::SMPS_MAX];
		static const Float32 CA_MIN_DIST;
		static const UInt32 LF_MIN_GHOST;
		static const UInt32 LF_MAX_GHOST;
		static const Mix::Matrix4x4 LF_CAM_BIAS_MAT0;
		static const Mix::Matrix4x4 LF_CAM_BIAS_MAT1;
		static const Float32 VIGNETTE_LEN;
	};

}}}
