using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace PostscreenEffects
{
    public class PostScreenFilters
    {
        private Effect              _LinearFilterEffect;        
        private Effect              _GaussianFilterEffect;
        private Effect              _AdditiveBlendEffect;
        private Effect              _DoFEffect;
        private Effect              _LightShafts;
        private Effect              _ExtractLuminance;
        private Effect              _VolumetricLighting;
        private int                 _Width;
        private int                 _Height;
        //private RenderTarget2D      _RenderTarget;
        private GraphicsDevice      _Device;
        private ChainedRenderTarget _CRT;
        private SpriteBatch         _SpriteBatch;

        // ---------------------------------------------------------
        public PostScreenFilters(
            int Width,
            int Height,
            GraphicsDevice Device,
            ContentManager Content )
        {
            _Device = Device;
            _SpriteBatch = new SpriteBatch( _Device );
            _Width = Width;
            _Height = Height;
            _LinearFilterEffect = Content.Load<Effect>( "Effects\\LinearFilter" );            
            _GaussianFilterEffect = Content.Load<Effect>( "Effects\\GaussianFilter" );
            _AdditiveBlendEffect = Content.Load<Effect>( "Effects\\AdditiveBlend" );
            _DoFEffect = Content.Load<Effect>( "Effects\\DepthOfField" );
            _LightShafts = Content.Load<Effect>( "Effects\\LightShafts" );
            _ExtractLuminance = Content.Load<Effect>( "Effects\\LuminanceFilter" );
            _VolumetricLighting = Content.Load<Effect>( "Effects\\VolumetricLighting" );
            
            //_RenderTarget = new RenderTarget2D( _Device,
            //    Width,
            //    Height,
            //    1,
            //    SurfaceFormat.HalfVector4 );

            _CRT = new ChainedRenderTarget(
                new SpriteBatch( _Device ),
                _LinearFilterEffect,
                _GaussianFilterEffect,
                _AdditiveBlendEffect,
                _Device,
                Width,
                Height );
        }
        // ---------------------------------------------------------
        public RenderTarget2D[ ] CreateMipMapLevels( 
            RenderTarget2D Source )
        {
            _CRT.RenderTarget = Source;
            _CRT.GenerateMipMapLevels( );
            return _CRT.GetMipLevels( );
        }
        // ---------------------------------------------------------
        public RenderTarget2D AdditiveBlend(
            RenderTarget2D Source )
        {
            _CRT.RenderTarget = Source;
            _CRT.GenerateMipMapLevels( );
            _CRT.ApplyBlur( );
            //_CRT.SaveMipLevels( "c:/", "daten" );
            _CRT.AdditiveBlend( );
            return _CRT.GetUpsampleTarget( );
        }
        // ---------------------------------------------------------
        public void AmbientOcclusion(
            RenderTarget2D Depthmap,
            RenderTarget2D Destination )
        {
            // TODO!!
        }
        // ---------------------------------------------------------
        public void DepthOfField(
            RenderTarget2D WorldCoordinates,
            RenderTarget2D Source,
            RenderTarget2D Destination,
            float Aperture,
            float ZNear,
            float ZFar,            
            float PlaneInFocus,
            float FocalLength  )
        {
            _CRT.RenderTarget = Source;
            _CRT.GenerateMipMapLevels( );
            _CRT.ApplyBlur( );
            _CRT.AdditiveBlend( );
            RenderTarget2D[ ] BlurLevels = _CRT.GetMipLevels( );

            /*
            _Device.SetRenderTarget( 0, Destination );
            _Device.Clear( Color.Black );
            _SpriteBatch.Begin(
                    SpriteBlendMode.None,
                    SpriteSortMode.Immediate,
                    SaveStateMode.SaveState );
            _SpriteBatch.Draw( BlurLevels[1].GetTexture( ),
                Vector2.Zero,
                Color.White );
            _SpriteBatch.End( );
            _Device.SetRenderTarget( 0, null );
            */
          
            _Device.SetRenderTarget( 0, Destination );
            _Device.Clear( ClearOptions.Target, Vector4.Zero, 1, 0 );

            _Device.Textures[ 0 ] = WorldCoordinates.GetTexture( );
            _Device.Textures[ 1 ] = Source.GetTexture( );
            _Device.Textures[ 2 ] = BlurLevels[ 0 ].GetTexture( );
            _DoFEffect.CurrentTechnique = _DoFEffect.Techniques[ 0 ];
            _DoFEffect.Parameters[ "ViewportSize" ].SetValue( new Vector2( _Width, _Height ) );
            _DoFEffect.Parameters[ "TextureSize" ].SetValue( new Vector2( _Width, _Height ) );
            _DoFEffect.Parameters[ "MatrixTransform" ].SetValue( Matrix.Identity );
            _DoFEffect.Parameters[ "gAperture" ].SetValue( Aperture );
            _DoFEffect.Parameters[ "gZNear" ].SetValue( ZNear );
            _DoFEffect.Parameters[ "gZFar" ].SetValue( ZFar );
            _DoFEffect.Parameters[ "gPlaneInFocus" ].SetValue( PlaneInFocus );
            _DoFEffect.Parameters[ "gFocalLength" ].SetValue( FocalLength );
            _DoFEffect.Begin( );
            foreach ( EffectPass pass in _DoFEffect.CurrentTechnique.Passes )
            {
                _SpriteBatch.Begin(
                    SpriteBlendMode.None,
                    SpriteSortMode.Immediate,
                    SaveStateMode.SaveState );
                pass.Begin( );
                _SpriteBatch.Draw( WorldCoordinates.GetTexture( ),
                    Vector2.Zero,
                    Color.White );
                pass.End( );
                _SpriteBatch.End( );
            } // foreach
            _DoFEffect.End( );
            _Device.Textures[ 0 ] = null;
            _Device.Textures[ 1 ] = null;
            _Device.Textures[ 2 ] = null;
            _Device.Textures[ 3 ] = null;
            _Device.Textures[ 4 ] = null;
            _Device.Textures[ 5 ] = null;
            _Device.Textures[ 6 ] = null;
            _Device.Textures[ 7 ] = null;
            _Device.SetRenderTarget( 0, null );
        }      
        // ---------------------------------------------------------
        public void LightShafts( 
            RenderTarget2D RenderTargetMask,
            RenderTarget2D Destination,
            Vector2 LightPos,            
            float Density,
            float Decay,
            float Weight,
            float Exposure )
        {
            _Device.SetRenderTarget( 0, Destination );
            _Device.Clear( ClearOptions.Target, Vector4.Zero, 1, 0 );

            _Device.Textures[ 0 ] = RenderTargetMask.GetTexture( );
            Effect effect = _LightShafts;
            effect.CurrentTechnique = effect.Techniques[ 0 ];

            effect.Parameters[ "ViewportSize" ].SetValue( new Vector2( _Width / 2, _Height / 2 ) );
            effect.Parameters[ "TextureSize" ].SetValue( new Vector2( _Width / 2, _Height / 2 ) );
            effect.Parameters[ "MatrixTransform" ].SetValue( Matrix.Identity );
            effect.Parameters[ "gScreenLightPos" ].SetValue( LightPos );
            effect.Parameters[ "gDensity" ].SetValue( Density );
            effect.Parameters[ "gDecay" ].SetValue( Decay );
            effect.Parameters[ "gWeight" ].SetValue( Weight );
            effect.Parameters[ "gExposure" ].SetValue( Exposure );
            effect.Begin( );
            foreach ( EffectPass pass in effect.CurrentTechnique.Passes )
            {
                _SpriteBatch.Begin(
                    SpriteBlendMode.None,
                    SpriteSortMode.Immediate,
                    SaveStateMode.SaveState );
                pass.Begin( );
                _SpriteBatch.Draw( RenderTargetMask.GetTexture( ),
                    Vector2.Zero,
                    Color.White );
                pass.End( );
                _SpriteBatch.End( );
            } // foreach
            effect.End( );

            _Device.SetRenderTarget( 0, null );
        }
        // ---------------------------------------------------------
        public void VolumetricLighting(
            RenderTarget2D WorldCoordinates,
            RenderTarget2D ShadowMap,
            RenderTarget2D Destination,
            Matrix LightView,
            Matrix LightProjection )
        {
            _Device.SetRenderTarget( 0, Destination );
            _Device.Clear( ClearOptions.Target, Vector4.Zero, 1, 0 );
            //_Device.Textures[ 0 ] = WorldCoordinates.GetTexture( );
            //_Device.Textures[ 1 ] = ShadowMap.GetTexture( );

            Matrix LightViewProjection = LightView * LightProjection;

            Effect effect = _VolumetricLighting;
            effect.CurrentTechnique = effect.Techniques[ 0 ];
            effect.Parameters[ "ViewportSize" ].SetValue( 
                new Vector2( _Width, _Height ) );
            effect.Parameters[ "TextureSize" ].SetValue( 
                new Vector2( _Width, _Height ) );
            effect.Parameters[ "MatrixTransform" ].SetValue( 
                Matrix.Identity );
            effect.Parameters[ "gLightViewProjectionXf" ].SetValue( 
                LightViewProjection );
            effect.Parameters[ "gTextureWorld" ].SetValue(
                WorldCoordinates.GetTexture( ) );
            effect.Parameters[ "gTextureShadow" ].SetValue(
                ShadowMap.GetTexture( ) );

            EffectPass pass = effect.CurrentTechnique.Passes[ 0 ];

            effect.Begin( );
            _SpriteBatch.Begin(
                SpriteBlendMode.None,
                SpriteSortMode.Immediate,
                SaveStateMode.SaveState );
            pass.Begin( );
            _SpriteBatch.Draw( WorldCoordinates.GetTexture( ), 
                Vector2.Zero, 
                Color.White );
            pass.End( );
            _SpriteBatch.End( );
            effect.End( );

            _Device.Textures[ 0 ] = null;
            _Device.Textures[ 1 ] = null;
            _Device.SetRenderTarget( 0, null );
        }
        // ---------------------------------------------------------
        public void ExtractHighLuminance(
            RenderTarget2D Source,
            RenderTarget2D Destination,
            float Threshold,
            float ScaleFactor )
        {
            _Device.SetRenderTarget( 0, Destination );
            _Device.Clear( ClearOptions.Target, Vector4.Zero, 1, 0 );

            _Device.Textures[ 0 ] = Source.GetTexture( );
            Effect effect = _ExtractLuminance;
            effect.CurrentTechnique = effect.Techniques[ 0 ];

            effect.Parameters[ "gThreshold" ].SetValue( Threshold );
            effect.Parameters[ "gScaleFactor" ].SetValue( ScaleFactor );
            effect.Begin( );
            EffectPass pass = effect.CurrentTechnique.Passes[ 0 ];

            _SpriteBatch.Begin(
                SpriteBlendMode.None,
                SpriteSortMode.Immediate,
                SaveStateMode.SaveState );
            pass.Begin( );
            _SpriteBatch.Draw( Source.GetTexture( ),
                Vector2.Zero,
                Color.White );
            pass.End( );
            _SpriteBatch.End( );

            effect.End( );

            _Device.SetRenderTarget( 0, null );
        }
        // ---------------------------------------------------------
        public void SixteenthToFullscreen( 
            RenderTarget2D Source,
            RenderTarget2D Destination )
        {
            /*
             * Indices:
             * [ 0 ] = Half
             * [ 1 ] = Quarter
             * [ 2 ] = Eighth
             */
            Rectangle ScreenSize = new Rectangle(
                0,
                0,
                ( int ) ( _Width / 4 ),
                ( int ) ( _Height / 4 ) );

            RenderTarget2D[ ] TempTargets = _CRT.GetTempLevels( );
            _Device.SetRenderTarget( 0, TempTargets[ 1 ] );
            _SpriteBatch.Begin( );
            _SpriteBatch.Draw(
                Source.GetTexture( ),
                ScreenSize,
                Color.White );
            _SpriteBatch.End( );
            _Device.SetRenderTarget( 0, null );

            ScreenSize = new Rectangle(
                0,
                0,
                ( int ) ( _Width / 2 ),
                ( int ) ( _Height / 2 ) );

            _Device.SetRenderTarget( 0, TempTargets[ 0 ] );
            _SpriteBatch.Begin( );
            _SpriteBatch.Draw(
                TempTargets[ 1 ].GetTexture( ),
                ScreenSize,
                Color.White );
            _SpriteBatch.End( );
            _Device.SetRenderTarget( 0, null );

            ScreenSize = new Rectangle(
                0,
                0,
                ( int ) _Width,
                ( int ) _Height );

            _Device.SetRenderTarget( 0, Destination );
            _SpriteBatch.Begin( );
            _SpriteBatch.Draw(
                TempTargets[ 0 ].GetTexture( ),
                ScreenSize,
                Color.White );
            _SpriteBatch.End( );
            _Device.SetRenderTarget( 0, null );
        }
        // ---------------------------------------------------------
        public void QuarterToFullscreen( 
            RenderTarget2D Source,
            RenderTarget2D Destination )
        {
            /*
             * Indices:
             * [ 0 ] = Half
             * [ 1 ] = Quarter
             * [ 2 ] = Eighth
             */

            Rectangle ScreenSize = new Rectangle(
                0,
                0,
                ( int ) ( _Width / 2 ),
                ( int ) ( _Height / 2 ) );

            RenderTarget2D[ ] TempTargets = _CRT.GetTempLevels( );

            _Device.SetRenderTarget( 0, TempTargets[ 0 ] );
            _SpriteBatch.Begin( );
            _SpriteBatch.Draw(
                Source.GetTexture( ),
                ScreenSize,
                Color.White );
            _SpriteBatch.End( );
            _Device.SetRenderTarget( 0, null );

            ScreenSize = new Rectangle(
                0,
                0,
                ( int ) _Width,
                ( int ) _Height );

            _Device.SetRenderTarget( 0, Destination );
            _SpriteBatch.Begin( );
            _SpriteBatch.Draw(
                TempTargets[ 0 ].GetTexture( ),
                ScreenSize,
                Color.White );
            _SpriteBatch.End( );
            _Device.SetRenderTarget( 0, null );
        }
        // ---------------------------------------------------------
        public void HalfToFullscreen( 
            RenderTarget2D Source,
            RenderTarget2D Destination )
        {
            /*
             * Indices:
             * [ 0 ] = Half
             * [ 1 ] = Quarter
             * [ 2 ] = Eighth
             */
            Rectangle ScreenSize = new Rectangle( 
                0,
                0,
                ( int ) _Width, 
                ( int ) _Height );
            RenderTarget2D[ ] TempTargets = _CRT.GetTempLevels( );
            _Device.SetRenderTarget( 0, Destination );
            _SpriteBatch.Begin( );
            _SpriteBatch.Draw(
                Source.GetTexture( ),
                ScreenSize,
                Color.White );
            _SpriteBatch.End( );
            _Device.SetRenderTarget( 0, null );
        }
        // ---------------------------------------------------------
    }
}
