using BloomPostprocess;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System;
using System.Collections.Generic;

namespace BloomTest
{    
    public class Game1 : Game
    {
        const int SCREENWIDTH = 1024, SCREENHEIGHT = 768;
        const bool FULLSCREEN = false;        
        GraphicsDeviceManager graphics;
        PresentationParameters pp;        
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;
        Texture2D background, shipImage, lazer_images;
        RenderTarget2D renderTarget1, renderTarget2;
        Song music; 

        Bloom bloom; // <--need to make this class still

        int bloomSettingsIndex = 0;              
        
        static public KeyboardState kb, lkb;
        static public GamePadState gp, lgp;
        static public MouseState ms, lms;
        static public int screenW, screenH;
        Rectangle screenRect;
        float BloomSatPulse = 1f, bloomSatDir=0.09f;
        Vector2 mp, pos, vel, aim, background_pos; //mouse pos, ship_pos, ship velocity, background position (scrolling)
        float rotation; // ship rotation
        
        // LAZERS VARS:
        List<Lazer> lazers = new List<Lazer>();
        Rectangle lazer1_rect, glow_rect;
        Vector2 lazer1_orig, glow_orig; 
        int lazer_timer = 0;
        static Random rand = new Random();
        SoundEffect lazerSound;

        // C O N S T R U C T        
        public Game1() {
            graphics = new GraphicsDeviceManager(this) { 
                PreferredBackBufferWidth = SCREENWIDTH, PreferredBackBufferHeight = SCREENHEIGHT, IsFullScreen = FULLSCREEN, PreferredDepthStencilFormat = DepthFormat.Depth16 };            
            Content.RootDirectory = "Content";
        }

        // I N I T         
        protected override void Initialize() {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            pp = GraphicsDevice.PresentationParameters; 
            screenW = pp.BackBufferWidth; screenH = pp.BackBufferHeight;
            screenRect = new Rectangle(0, 0, screenW, screenH);

            bloom = new Bloom(GraphicsDevice, spriteBatch);
            renderTarget1 = new RenderTarget2D(GraphicsDevice, screenW, screenH, false, pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount, RenderTargetUsage.DiscardContents);
            renderTarget2 = new RenderTarget2D(GraphicsDevice, screenW, screenH, false, pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount, RenderTargetUsage.DiscardContents);

            lazer1_rect = new Rectangle(79, 15, 32, 32);   lazer1_orig = new Vector2(16f, 16f);
            glow_rect   = new Rectangle(10, 10, 44, 44);   glow_orig   = new Vector2(22f, 22f);
            base.Initialize();
        }
        
        // L O A D 
        protected override void LoadContent() {                      
            spriteFont = Content.Load<SpriteFont>("hudFont");            
            background = Content.Load<Texture2D>("background");
            shipImage = Content.Load<Texture2D>("space_ship");
            lazer_images = Content.Load<Texture2D>("lazers");
            bloom.LoadContent(Content, pp);
            lazerSound = Content.Load<SoundEffect>("shootLazer");
            music = Content.Load<Song>("subterain1");                        
            MediaPlayer.Play(music); MediaPlayer.IsRepeating = true;
        }        
        protected override void UnloadContent() {
            bloom.UnloadContent();
            renderTarget1.Dispose(); renderTarget2.Dispose();
        }

        // U P D A T E
        enum gameControl { mouse, gamepad };
        gameControl useController = gameControl.mouse;        
        protected override void Update(GameTime gameTime) {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();
            lkb = kb; lgp = gp; lms = ms; lgp = gp;
            kb = Keyboard.GetState(); ms = Mouse.GetState(); gp = GamePad.GetState(PlayerIndex.One);            

            // Switch to the next bloom settings preset?
            if ((gp.Buttons.A == ButtonState.Pressed && lgp.Buttons.A != ButtonState.Pressed) || (kb.IsKeyDown(Keys.A) && lkb.IsKeyUp(Keys.A)))  {
                bloomSettingsIndex = (bloomSettingsIndex + 1) % BloomSettings.PresetSettings.Length;
                bloom.Settings = BloomSettings.PresetSettings[bloomSettingsIndex];                
            }  
            // Cycle through the intermediate buffer debug display modes?
            if ((gp.Buttons.X == ButtonState.Pressed && lgp.Buttons.X != ButtonState.Pressed) || (kb.IsKeyDown(Keys.X) && lkb.IsKeyUp(Keys.X)))  {                
                bloom.ShowBuffer++;
                if (bloom.ShowBuffer > Bloom.IntermediateBuffer.FinalResult)   bloom.ShowBuffer = 0;
            }            

            if (gp.Buttons.Start == ButtonState.Pressed) useController = gameControl.gamepad;
            if (ms.MiddleButton == ButtonState.Pressed) useController = gameControl.mouse;

            // SHIP CONTROL / MOVEMENT            
            if (useController == gameControl.mouse)
            {
                mp = new Vector2(ms.X, ms.Y);
                vel = (mp - pos) * 0.2f; // adjust velocity by 20% of the distance movement of mouse each frame (makes smooth movements)   
                vel.X = MathHelper.Clamp(vel.X, -20.0f, 20.0f);
                vel.Y = MathHelper.Clamp(vel.Y, -20.0f, 20.0f);
            }
            else if (useController == gameControl.gamepad) {                 
                vel = gp.ThumbSticks.Left * 20f;
                vel.Y *= -1;                    
            }            
            pos += vel; background_pos += vel;
            Vector2 direction = vel; //vector between mouse point and player position            
            pos.X = MathHelper.Clamp(pos.X, screenW * .4f, screenW - screenW * .4f);
            pos.Y = MathHelper.Clamp(pos.Y, screenH * .4f, screenH - screenH * .4f);
            
            if (direction.LengthSquared() > 0.1f)
            {
                direction.Normalize();                                  //Vector2.Normalize(direction); // <-- DON'T DO THIS
                aim = direction; 
                rotation = (float)Math.Atan2(direction.Y, direction.X);
            }                            
            
            // LAZERS: 
            if (lazer_timer<=0) {                
                if ((gp.Buttons.B == ButtonState.Pressed && lgp.Buttons.B != ButtonState.Pressed) || (kb.IsKeyDown(Keys.Space) && lkb.IsKeyUp(Keys.Space))) {
                    Quaternion aimQuat = Quaternion.CreateFromYawPitchRoll(0, 0, rotation); 
                    Vector2 offset = Vector2.Transform(new Vector2(25, -8), aimQuat); //rotate to point on gun on ship at angle aimQuat                    
                    lazers.Add(new Lazer(pos + offset, vel+aim*10f)); //add bullet at gun1 at correct velocity (ship velocity + lazer velocity)                    
                    offset = Vector2.Transform(new Vector2(25, 8), aimQuat);
                    lazers.Add(new Lazer(pos + offset, vel+aim * 10f)); //add bullet at gun1 at correct velocity (ship velocity + lazer velocity)                    
                    lazerSound.Play(0.2f, RandomFloat(-0.2f, 0.2f), 0);
                    lazer_timer=5;
                }
            } else lazer_timer--;
            foreach (var lazer in lazers) lazer.Update(vel); // (vel is provided to adjust positions based on world position change too)
            //remove any expired lazers:            
            lazers.RemoveAll(item => item.expired == true); //Console.WriteLine("lazer count = "+lazers.Count);

            //Just for fun - here I've added a pulse animation by adjusting the bloom saturation:
            BloomSatPulse += bloomSatDir;
            if (BloomSatPulse > 2.5f) bloomSatDir = -0.09f;
            if (BloomSatPulse < 0.1f) bloomSatDir = 0.09f;
            bloom.Settings.BloomSaturation = BloomSatPulse;

            base.Update(gameTime);
        }

        
        // helper for rand floats:
        float RandomFloat(float min, float max) {
            return (float)rand.NextDouble() * (max - min) + min;
        }


        // D R A W
        protected override void Draw(GameTime gameTime) {            
            const bool bloom_background = false;

            GraphicsDevice.SetRenderTarget(renderTarget1);
            GraphicsDevice.Clear(Color.TransparentBlack); // <-- need this too
            if (bloom_background) {
                spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.LinearWrap); // <--- using linear WRAP
                spriteBatch.Draw(background, screenRect, new Rectangle((int)background_pos.X, (int)background_pos.Y, background.Width, background.Height), Color.White);
                spriteBatch.End();
            }
            DrawShip();
            DrawLazers();
            bloom.Draw(renderTarget1, renderTarget2);

            GraphicsDevice.SetRenderTarget(null);                        
            
            // draw wrapped background: 
            if (bloom_background == false) {
                spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.LinearWrap); // <--- using linear WRAP
                spriteBatch.Draw(background, screenRect, new Rectangle((int)background_pos.X, (int)background_pos.Y, background.Width, background.Height), Color.White);
                spriteBatch.End();            
            }
        
            // Draw bloomed layer over top: 
            spriteBatch.Begin(0, BlendState.AlphaBlend);
            spriteBatch.Draw(renderTarget2, new Rectangle(0, 0, screenW, screenH), Color.White); // draw all glowing components            
            spriteBatch.End();

            DrawOverlayText();

            base.Draw(gameTime);
        }


        // D R A W   S H I P 
        void DrawShip()
        {            
            spriteBatch.Begin();            
            spriteBatch.Draw(shipImage, pos, null, Color.White, rotation, new Vector2(shipImage.Width/2, shipImage.Height/2), 1.5f, SpriteEffects.None, 0f);
            spriteBatch.End();            
        }

        // D R A W   L A Z E R S
        void DrawLazers()
        {
            if (lazers.Count < 1) return;
            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullNone);
            int i = 0; 
            do {
                spriteBatch.Draw(lazer_images, lazers[i].pos, lazer1_rect, Color.White, lazers[i].rot, lazer1_orig, lazers[i].scale, SpriteEffects.None, 0f);
                spriteBatch.Draw(lazer_images, lazers[i].pos, glow_rect,   Color.Red, lazers[i].rot,   glow_orig, lazers[i].scale, SpriteEffects.None, 0f);
                i++;
            } while (i < lazers.Count); // could use foreach too (shouldn't impact performance noticably)
            spriteBatch.End();
        }

        // D R A W   O V E R L A Y   T E X T 
        void DrawOverlayText()
        {
            string text = "A = settings (" + bloom.Settings.Name + ")\n" +
                            "X = show buffer (" + bloom.ShowBuffer.ToString() + ")\n" +
                            "GamePad B = shoot or   SpaceBar = shoot \n" +
                            "Start = GamePad   or   Mid Mouse Click = Mouse";

            spriteBatch.Begin();
            
            spriteBatch.DrawString(spriteFont, text, new Vector2(65, 65), Color.Black);
            spriteBatch.DrawString(spriteFont, text, new Vector2(64, 64), Color.White);

            spriteBatch.End();
        }

    }
}
