using System;
using System.Collections.Generic;
using System.Text;
using Vortex;
using Vortex.Drawing;
using Vortex.Input;

namespace Vortex.Tutorials.GameDemo
{

    class LightSource
    {
        public float Intensity=2.0f;
        public Vector3 Position;
        public Vector3 Color;
    }

    class Material
    {
        public float Exp = 10f;
        public float Diffuse = 0.7f;
        public float Specular = 0.7f;
    }

    class FireDistortionDemo : Game
    {
        Texture _DiffuseTexture;
        Texture _NormalTexture;
        PixelShader _PhongBlinnShader;
        PixelShader _DistortPixelShader;

        TargetTexture _DistortTarget;
        TargetTexture _DiffuseTarget;


        LightSource LightSource;
        Material Material;

        Vector4 AmbientColor;
        float AmbientPower;

        float Refraction = 12.0f;


        FireSystem FireSystem;
        FireSystem DistortFireSystem;

        int ShowRT = 0;

        bool DrawHelp=false;
        int WindowW = 800;
        int WindowH = 600;
        float WindowHalfW;
        float WindowHalfH;

        public FireDistortionDemo()
        {
            Game.Cursor = GameCursor.Blank;
            WindowHalfW = WindowW / 2.0f;
            WindowHalfH = WindowH / 2.0f;

            Window.Size = new System.Drawing.Size(WindowW, WindowH);            
            Window.VerticalSync = true;
            ChangeDisplayModeOnAltEnter = false;

            LightSource = new LightSource();
            LightSource.Position = new Vector3(WindowHalfW, WindowHalfH, 60);
            LightSource.Color = new Vector3(1,0.6f,0.3f);

            Material = new Material();

            AmbientColor = new Vector4(0.3f, 0.5f, 1.0f,0.6f);
            AmbientPower = 0.5f;

            // 30 sprites for small fire would be fine
            FireSystem = new FireSystem(30);
            // 10 sprites for distort is ok too ;)
            DistortFireSystem = new FireSystem(10);

            _DistortTarget = new TargetTexture(WindowW, WindowH);
            _DiffuseTarget = new TargetTexture(WindowW, WindowH);

            Keyboard.KeyUp += delegate(VirtualKey key)
            {
                if (key == VirtualKey.Escape)
                {
                    Terminate();
                }
            };

            #region Keyboard.OnKeyDown stuff
            Keyboard.KeyDown += delegate(VirtualKey key)
            {
                switch (key)
                {
                    case VirtualKey.F1:
                        DrawHelp = !DrawHelp;
                        break;
                    case VirtualKey.D1:
                        ShowRT = 1;
                        break;
                    case VirtualKey.D2:
                        ShowRT = 2;
                        break;
                    case VirtualKey.D3:
                        ShowRT = 0;
                        break;
                    case VirtualKey.Q:
                        Material.Exp = Math.Min(Material.Exp + 0.5f, 60f);
                        break;
                    case VirtualKey.A:
                        Material.Exp = Math.Max(Material.Exp - 0.5f, 0f);
                        break;
                    case VirtualKey.W:
                        Material.Diffuse = Math.Min(Material.Diffuse + 0.1f, 1f);
                        break;
                    case VirtualKey.S:
                        Material.Diffuse = Math.Max(Material.Diffuse - 0.1f, 0f);
                        break;
                    case VirtualKey.E:
                        Material.Specular = Math.Min(Material.Specular + 0.1f, 1f);
                        break;
                    case VirtualKey.D:
                        Material.Specular = Math.Max(Material.Specular - 0.1f, 0f);
                        break;
                    case VirtualKey.R:
                        AmbientPower = Math.Min(AmbientPower + 0.05f, 1f);
                        break;
                    case VirtualKey.F:
                        AmbientPower = Math.Max(AmbientPower - 0.05f, 0f);
                        break;
                    case VirtualKey.T:
                        LightSource.Position.Z = Math.Min(LightSource.Position.Z + 1f, 200f);
                        break;
                    case VirtualKey.G:
                        LightSource.Position.Z = Math.Max(LightSource.Position.Z - 1f, 0f);
                        break;
                    case VirtualKey.Y:
                        LightSource.Intensity = Math.Min(LightSource.Intensity + 0.1f, 10f);
                        break;
                    case VirtualKey.H:
                        LightSource.Intensity = Math.Max(LightSource.Intensity - 0.1f, 0f);
                        break;
                    case VirtualKey.U:
                        Refraction = Math.Min(Refraction + 0.5f, 500f);
                        break;
                    case VirtualKey.J:
                        Refraction = Math.Max(Refraction - 0.5f, 1f);
                        break;
                }
            };
            #endregion

            Mouse.Move += delegate(MouseMoveActionInfo info)
            {
                LightSource.Position = new Vector3(info.Location.X,info.Location.Y,LightSource.Position.Z);

            };
        }

        protected override void Load()
        {
			Vortex.IO.FileSystem.SetPathAlias("demodata", "./data/FireDemo");
            // Bla-bla-bla
			_DiffuseTexture = new Texture(@"{demodata}/diffuse.jpg");
			_NormalTexture = new Texture(@"{demodata}/normal.jpg");
			_PhongBlinnShader = new PixelShader(@"{demodata}/phongblinn.psh");
			_DistortPixelShader = new PixelShader(@"{demodata}/distort.psh");

            #region My own creepy particle system for fire configuration
			FireSystem.AddSprite(@"{demodata}/fire1.png");
			FireSystem.AddSprite(@"{demodata}/fire2.png");
			FireSystem.AddSprite(@"{demodata}/fire3.png");
			FireSystem.AddCoverSprite(@"{demodata}/firecover.png");
            FireSystem.ColorGradient.AddColor(0.0f,ColorU.White);
            FireSystem.ColorGradient.AddColor(0.2f, ColorU.Gold);
            FireSystem.ColorGradient.AddColor(0.7f, ColorU.Red);
            FireSystem.ColorGradient.AddColor(1.0f, ColorU.Black);
            FireSystem.Blending = Blending.Add;
            FireSystem.ParticleLife = 300;
            FireSystem.ParticleSpeed= 0.2f;
            #endregion
            FireSystem.Init();

            #region Fire distort system
            DistortFireSystem.StartSize = 0.6f;
            DistortFireSystem.EndSize = 0.5f;
            DistortFireSystem.StartAlpha = 0.4f;
            DistortFireSystem.ParticleSpeed = 0.3f;
            DistortFireSystem.ParticleLife = 400;
			DistortFireSystem.AddSprite(@"{demodata}/firedistort.png");
            #endregion
            DistortFireSystem.Init();

        }

        protected override void Unload()
        {
            #region Killing stuff
            _DistortTarget.Dispose();
            _DiffuseTexture.Dispose();
            _NormalTexture.Dispose();
            _PhongBlinnShader.Dispose();
            _DistortPixelShader.Dispose();
            FireSystem.Dispose();
            DistortFireSystem.Dispose();
            _DiffuseTarget.Dispose();
            #endregion
        }

        protected override void Update(GameTime time)
        {
            LightSource.Intensity = Vortex.Utils.RandomUtils.NextFloat(0.7f, 1.5f);
            FireSystem.Source = new Vector2(Mouse.Location.X,Mouse.Location.Y);
            DistortFireSystem.Source = FireSystem.Source;
            FireSystem.Update(time);
            DistortFireSystem.Update(time);
        }

        private void SetLightParameters()
        {
            #region A lot of lighting parameters
            // Passing light parameters
            _PhongBlinnShader.Params.Set("LightColor", LightSource.Color);
            _PhongBlinnShader.Params.Set("LightPosition", LightSource.Position);
            _PhongBlinnShader.Params.Set("Intensity", LightSource.Intensity);

            // Passing material parameters
            _PhongBlinnShader.Params.Set("Exp", Material.Exp);
            _PhongBlinnShader.Params.Set("DiffusePower", Material.Diffuse);
            _PhongBlinnShader.Params.Set("SpecularPower", Material.Specular);

            // Passing object parameters
            _PhongBlinnShader.Params.Set("ObjectSize", new Vector2(_DiffuseTexture.Size.Width, _DiffuseTexture.Size.Height));
            _PhongBlinnShader.Params.Set("ObjPosition", new Vector2(WindowHalfW - _DiffuseTexture.Size.Width / 2, WindowHalfH - _DiffuseTexture.Size.Height / 2));

            // Passing ambient light parameters
            _PhongBlinnShader.Params.Set("AmbientColor", AmbientColor);
            _PhongBlinnShader.Params.Set("AmbientPower", AmbientPower);
            #endregion
        }

        private void SetDistortParameters()
        {
            // Passing distort shader params
            _DistortPixelShader.Params.Set("Size", new Vector2(WindowW / Refraction, WindowH / Refraction));
        }

        private void RenderDiffuse(Canvas2D canvas)
        {
            canvas.Clear(ColorU.Black);
            using (canvas <= _PhongBlinnShader <= _NormalTexture * 2)
            {
                canvas.DrawSprite(new Vector2(WindowHalfW, WindowHalfH), _DiffuseTexture.ToSprite(), ColorU.White);
            }
        }

        private void RenderDistort(Canvas2D canvas)
        {
            canvas.Clear(new ColorU(0.5f, 0.5f, 0.5f, 1.0f));
            DistortFireSystem.Render(canvas);
        }

        private void RenderFinal(Canvas2D canvas)
        {
            using (canvas <= _DistortPixelShader <= _DistortTarget * 2)
            {
                canvas.DrawTexture(canvas.Region, _DiffuseTarget, ColorU.White);
            }            
        }

        private void RenderParticles(Canvas2D canvas)
        {
            FireSystem.Render(canvas);
        }

        protected override void Render(Canvas2D canvas)
        {
            SetLightParameters();
            SetDistortParameters();

            switch (ShowRT)
            {
                case 0: // This is rendering final image
                    RenderDiffuse(_DiffuseTarget.Canvas);   // First rendering diffuse to diffuse RT
                    RenderDistort(_DistortTarget.Canvas);   // Then distort to distort RT
                    RenderFinal(canvas);                    // Rendering diffuse RT with distort shader
                    RenderParticles(canvas);                // And particles on top of it
                    break;
                case 1: // Render diffuse without distort
                    RenderDiffuse(canvas);
                    RenderParticles(canvas);
                    break;
                case 2: // Render only distort RT (sometimes it's good to know how it looks like)
                    RenderDistort(canvas);
                    break;
            }            

            if (DrawHelp)
            {
                #region Help
                canvas.DrawTextLayout(
                    SpriteFont.SmallConsole,
                    canvas.Region.Deflate(new Vector2(4, 4)),
                    string.Format(
                    "1, 2, 3 - Show Diffuse RT, Distort RT, Final image\n" +
                    "Q, A - Material specular exponent (Value = {0:F2})\n" +
                    "W, S - Material diffuse power (Value = {1:F2})\n" +
                    "E, D - Material specular power (Value = {2:F2})\n" +
                    "R, F - Ambient power (Value = {3:F2})\n" +
                    "T, G - Light source Z position (Value = {4:F2})\n" +
                    "Y, H - Light intensity (Value = {5:F2})\n" +
                    "U, J - Something like refraction value (Value = {6:F2})\n" +
                    "P - Play/Pause sound \n\n" +
                    "Esc - Exit"
                    ,
                    Material.Exp, Material.Diffuse, Material.Specular, AmbientPower, LightSource.Position.Z, LightSource.Intensity, Refraction),
                    TextLayout.Left | TextLayout.Bottom,
                    ColorU.White
                );
                #endregion
            }
            else
            {
                #region How to show help?
                canvas.DrawTextLayout(
                    SpriteFont.SmallConsole,
                    canvas.Region.Deflate(new Vector2(4, 4)),
                    "F1 - Help",
                    TextLayout.Left | TextLayout.Bottom,
                    ColorU.White
                );
                #endregion
            }

        }
    }

}
