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

namespace LD20_Game
{
    class Level19Scene : BaseScene
    {
        // Level elements
        PlayerCharacter player;
        List<Platform> platforms = new List<Platform>();
        List<Ladder> ladders = new List<Ladder>();
        List<BaseTrap> traps = new List<BaseTrap>();
        ExitDoor exit;
        Vector2 signLocation;
        Buddi buddi;

        Vector2 lampDirection = Directions.Right;
        bool lightsOff;

        public Level19Scene()
        {
            MusicManager.Play("cave2");

            player = new PlayerCharacter(new Vector2(40, 500));
            buddi = new Buddi();

            platforms.Add(new Platform(new Vector2(0, 575)));
            platforms.Add(new Platform(new Vector2(0, 419)));
            platforms.Add(new Platform(new Vector2(0, 263)));
            platforms.Add(new Platform(new Vector2(0, 107)));

            ladders.Add(new Ladder(615, 418));
            ladders.Add(new Ladder(50, 262));

            traps.Add(new SpikeTrap(420, 566));
            traps.Add(new MudslideTrap(310, 263));
            traps.Add(new SpikeTrap(400, 255));

            exit = new ExitDoor(600, 151);

            signLocation = new Vector2(100, 460);

            lightsOff = true;
        }

        private void UpdateLampDirection(GamePadState gs, KeyboardState ks)
        {
            if (InputManager.ControlMethod == ControlMethod.Keyboard)
            {
                MouseState ms = Mouse.GetState();

                lampDirection = new Vector2(ms.X - player.HeadLocation.X, -(ms.Y - player.HeadLocation.Y));                        
            }

            if (InputManager.ControlMethod == ControlMethod.Gamepad)
            {
                if (gs.ThumbSticks.Right == Vector2.Zero)
                {
                    if (player.Mirrored)
                        lampDirection = Directions.Left;
                    else
                        lampDirection = Directions.Right;
                }
                else
                {
                    Vector2 input = gs.ThumbSticks.Right;

                    if (player.Mirrored && (Math.Sign(gs.ThumbSticks.Right.X) == 1))
                        input = Directions.Left;

                    if (!player.Mirrored && (Math.Sign(gs.ThumbSticks.Right.X) == -1))
                        input = Directions.Right;

                    lampDirection = input;
                }
            }
        }

        private bool PlayerPressingRight(GamePadState gs, KeyboardState ks)
        {
            if (InputManager.ControlMethod == ControlMethod.Gamepad)
            {
                if (gs.IsButtonDown(Buttons.DPadRight))
                    return true;

                if (gs.IsButtonDown(Buttons.LeftThumbstickRight))
                    return true;
            }

            if (InputManager.ControlMethod == ControlMethod.Keyboard)
            {

                if (ks.IsKeyDown(Keys.Right))
                    return true;

                if (ks.IsKeyDown(Keys.D))
                    return true;
            }

            return false;
        }

        private bool PlayerPressingLeft(GamePadState gs, KeyboardState ks)
        {
            if (InputManager.ControlMethod == ControlMethod.Gamepad)
            {
                if (gs.IsButtonDown(Buttons.DPadLeft))
                    return true;

                if (gs.IsButtonDown(Buttons.LeftThumbstickLeft))
                    return true;
            }

            if (InputManager.ControlMethod == ControlMethod.Keyboard)
            {
                if (ks.IsKeyDown(Keys.Left))
                    return true;

                if (ks.IsKeyDown(Keys.A))
                    return true;
            }

            return false;
        }

        private void CheckForPlayerMovement(GameTime gameTime, GamePadState gs, KeyboardState ks)
        {
            if (PlayerPressingLeft(gs, ks))
            {
                player.MoveLeft(gameTime);
            }

            if (PlayerPressingRight(gs, ks))
            {
                player.MoveRight(gameTime);
            }

            if (PlayerPressingJump(gs, ks))
            {
                if (player.Bounds.Intersects(exit.Bounds))
                {
                    SoundManager.Play("leveldone");
                    SceneManager.TransitionTo(new Level18Scene());
                    return;
                }

                if (!player.Falling)
                {
                    SoundManager.Play("jump");
                    Vector2 boost = GetBoostFromInput(gs,ks);
                    player.Jump(boost);
                }
            }

            if (PlayerPressingClimb(gs, ks))
            {
                if (PlayerTouchingALadder())
                    player.Ascend();
            }

            if (PlayerPressingDescend(gs, ks))
            {
                if (PlayerTouchingALadder() || PlayerAboveALadder())
                    player.Descend();
            }
        }

        private bool PlayerAboveALadder()
        {
            foreach (Ladder ladder in ladders)
            {
                if (ladder.Overhead.Intersects(player.Bounds))
                    return true;
            }

            return false;
        }

        private bool PlayerPressingDescend(GamePadState gs, KeyboardState ks)
        {
            if (InputManager.ControlMethod == ControlMethod.Gamepad)
            {
                if (gs.IsButtonDown(Buttons.DPadDown))
                    return true;

                if (gs.IsButtonDown(Buttons.LeftThumbstickDown))
                    return true;
            }

            if (InputManager.ControlMethod == ControlMethod.Keyboard)
            {
                if (ks.IsKeyDown(Keys.Down) || ks.IsKeyDown(Keys.S))
                    return true;
            }
            return false;
        }

        private bool PlayerTouchingALadder()
        {
            foreach (Ladder ladder in ladders)
            {
                if (ladder.Bounds.Intersects(player.Bounds))
                    return true;
            }

            return false;
        }

        private bool PlayerPressingClimb(GamePadState gs, KeyboardState ks)
        {
            if (InputManager.ControlMethod == ControlMethod.Gamepad)
            {
                if (gs.IsButtonDown(Buttons.DPadUp))
                    return true;

                if (gs.IsButtonDown(Buttons.LeftThumbstickUp))
                    return true;
            }

            if (InputManager.ControlMethod == ControlMethod.Keyboard)
            {
                if (ks.IsKeyDown(Keys.Up) || ks.IsKeyDown(Keys.W))
                    return true;
            }

            return false;
        }

        private Vector2 GetBoostFromInput(GamePadState gs, KeyboardState ks)
        {
            float maxBoost = 4.0f;

            if (ks.IsKeyDown(Keys.A) || ks.IsKeyDown(Keys.Left))
                return new Vector2(-maxBoost, 0);

            if (ks.IsKeyDown(Keys.D) || ks.IsKeyDown(Keys.Right))
                return new Vector2(maxBoost, 0);

            if (gs.IsButtonDown(Buttons.DPadLeft))
                return new Vector2(-maxBoost, 0);

            if (gs.IsButtonDown(Buttons.DPadRight))
                return new Vector2(maxBoost, 0);

            if (gs.IsButtonDown(Buttons.LeftThumbstickLeft))
                return new Vector2(-maxBoost, 0);

            if (gs.IsButtonDown(Buttons.LeftThumbstickRight))
                return new Vector2(maxBoost, 0);

            if (gs.IsButtonDown(Buttons.LeftThumbstickRight))
                return new Vector2(maxBoost, 0);

            return Vector2.Zero;
        }

        private bool PlayerPressingJump(GamePadState gs, KeyboardState ks)
        {

            if (InputManager.ControlMethod == ControlMethod.Gamepad)
            {
                if (gs.IsButtonDown(Buttons.A) && lastPad.IsButtonUp(Buttons.A))
                    return true;

                if (gs.IsButtonDown(Buttons.RightShoulder) && lastPad.IsButtonUp(Buttons.RightShoulder))
                    return true;
            }


            if (InputManager.ControlMethod == ControlMethod.Keyboard)
            {
                if (ks.IsKeyDown(Keys.Space) && lastKeyboard.IsKeyUp(Keys.Space))
                    return true;
            }

            return false;
        }

        GamePadState lastPad;
        KeyboardState lastKeyboard;
        public override void UpdateScene(GameTime gameTime)
        {
            GamePadState gs = GamePad.GetState(InputManager.PlayerIndex);
            KeyboardState ks = Keyboard.GetState();  

            CheckForPlayerMovement(gameTime, gs, ks);
            UpdateLampDirection(gs, ks);
            if (!PlayerTouchingALadder())
                player.SuccumbToGravity();
            else
                player.StopFalling();

            player.Update(gameTime);

            CheckForPlatformLands(gameTime, gs, ks);

            ClampPlayerOntoMap();

            buddi.HoverOver(player.Location);
            buddi.Update(gameTime);

            CheckPlayerTouchedAlert(gameTime);
            UpdateAllTraps(gameTime);
            CheckPlayerHitATrap();

            lastPad = gs;
            lastKeyboard = ks;
            base.UpdateScene(gameTime);
        }

        private void UpdateAllTraps(GameTime gameTime)
        {
            foreach (BaseTrap trap in traps)
                trap.Update(gameTime);
        }

        private void CheckPlayerHitATrap()
        {
            foreach (BaseTrap trap in traps)
            {
                if (player.Bounds.Intersects(trap.FatalArea))
                {
                    // DIE INSTANTLY
                    SoundManager.Play("dead");
                    SceneManager.TransitionTo(new Level19Scene());
                }
            }
        }

        private void CheckPlayerTouchedAlert(GameTime gameTime)
        {
            foreach (BaseTrap trap in traps)
            {
                foreach (BoundingBox alertBox in trap.Alerts)
                {
                    if (player.Bounds.Intersects(alertBox))
                    {
                        if (gameTime.TotalGameTime > buddi.nextAlertAt)
                        {
                            SoundManager.PlayRandomWarning();
                            buddi.RaiseAlert(trap.Type, gameTime);

                            if (trap.Type == TrapType.Mudslide)
                            {
                                MudslideTrap ms = (MudslideTrap)trap;
                                ms.Activate(gameTime);
                            }

                        }
                    }
                }
            }
        }

        private void ClampPlayerOntoMap()
        {
            if (player.Location.X < 0)
                player.GoTo(0, (int)player.Location.Y);
                

            if (player.Location.Y < 0)
                player.GoTo((int)player.Location.X, 0);

            if (player.Location.X >= 765) // hardcoded, short on time, don't care
                player.GoTo(764, (int)player.Location.Y);

            if (player.Location.Y >= 600 - 75)
                player.GoTo((int)player.Location.X, 599);
        }

        private bool PlayerIsAbovePlatform(Platform plat)
        {
            return ((player.FootLocation.Y - 16) < plat.Bounds.Min.Y);
        }

        private void CheckForPlatformLands(GameTime gameTime, GamePadState gs, KeyboardState ks)
        {
            foreach (Platform plat in platforms)
            {
                if (plat.Bounds.Intersects(player.Bounds) &&
                    PlayerIsAbovePlatform(plat) && !(PlayerAboveALadder() && PlayerPressingDescend(gs, ks)))
                {
                    player.StandAt(new Vector2(player.Location.X, plat.Location.Y));
                    player.StopFalling();
                    return;
                }
            }

            player.Falling = true;
        }

        public override void DrawScene(SpriteBatch spriteBatch, GameTime gameTime)
        {
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);

            spriteBatch.Draw(TextureManager.Textures["minebg"], Vector2.Zero, Color.White);

            exit.DrawDoor(spriteBatch);

            DrawAllPlatforms(spriteBatch);

            DrawAllLadders(spriteBatch);

            DrawAllTraps(spriteBatch);

            spriteBatch.Draw(TextureManager.Textures["level19sign"], signLocation, Color.White);

            if (lightsOff)
                DrawLampOverlay(spriteBatch);

            exit.DrawSign(spriteBatch);

            player.Draw(spriteBatch, gameTime);
            buddi.Draw(spriteBatch);

            if (InputManager.ControlMethod == ControlMethod.Keyboard)
                DrawMouseCursor(spriteBatch);

            base.DrawScene(spriteBatch, gameTime);

            spriteBatch.End();
        }

        private void DrawMouseCursor(SpriteBatch spriteBatch)
        {
            // 24 = half the cursor's width of 48. ARGH hardcoding.
            MouseState ms = Mouse.GetState();

            Vector2 drawPos = new Vector2(ms.X - 24, ms.Y - 24);
            spriteBatch.Draw(TextureManager.Textures["mousecursor"], drawPos, Color.White);
        }

        private void DrawAllTraps(SpriteBatch spriteBatch)
        {
            foreach (BaseTrap trap in traps)
            {
                trap.Draw(spriteBatch);
            }
        }

        private void DrawLampOverlay(SpriteBatch spriteBatch)
        {
            float angle = (float)(Math.Atan2(lampDirection.X, lampDirection.Y));

            Rectangle screenPos = new Rectangle((int)player.HeadLocation.X,
                                                (int)player.HeadLocation.Y,
                                                2000, 2000);
            spriteBatch.Draw(TextureManager.Textures["lampoverlay"], screenPos, null, Color.White,
                              angle, new Vector2(1000, 1000), SpriteEffects.None, 0);

        }

        private void DrawAllPlatforms(SpriteBatch spriteBatch)
        {
            foreach (Platform plat in platforms)
            {
                plat.Draw(spriteBatch);
            }
        }

        private void DrawAllLadders(SpriteBatch spriteBatch)
        {
            foreach (Ladder ladder in ladders)
            {
                ladder.Draw(spriteBatch);
            }
        }
    }
}
