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 IntroLevel : BaseScene
    {
        static Vector2 fakeBuddiLocation = new Vector2(-30, -8);

        // States etc
        bool buddiTaken = false;
        bool lightsOff;

        // NOTE: Yes I know this is terrible but I'll abstract the OO later and come back to this part
        // if I remember and have time. There's a 48 hour deadline, people!
        PlayerCharacter player;
        List<Platform> platforms = new List<Platform>();
        List<CollisionTrigger> triggers = new List<CollisionTrigger>();
        ExitDoor exit;
        HelperArrow arrow;
        BoundingBox unusedBuddiLocation;
        //-
        Vector2 fakeBuddiOffset = Vector2.Zero;
        float fakeBuddipathSpeed = 40;
        float fakeBuddipathLength = 20;
        //-
                                                            
        Vector2 lampDirection = Directions.Right;

        bool playerIsFrozen;
        TimeSpan canMoveAgainAt = TimeSpan.MinValue;

        public IntroLevel()
        {
            MusicManager.Play("intro");

            player = new PlayerCharacter(new Vector2(200, 450));

            platforms.Add(new Platform(new Vector2(0, 560)));

            BoundingBox collapseTrigger = new BoundingBox(new Vector3(600, 0, 0), new Vector3(605, 600, 0));
            triggers.Add(new CollisionTrigger(collapseTrigger, StartCollapse));

            exit = new ExitDoor(675, 450);
            arrow = new HelperArrow(59, 380, false);

            unusedBuddiLocation = new BoundingBox(new Vector3(109, 513, 0),
                                                           new Vector3(109 + 28, 513 + 28, 0));

            lightsOff = false;
        }

        GamePadState lastPad;
        KeyboardState lastKeyboard;
        public override void UpdateScene(GameTime gameTime)
        {
            UpdateFakeBuddiPosition(gameTime);

            if (playerIsFrozen && canMoveAgainAt == TimeSpan.MinValue)
                canMoveAgainAt = gameTime.TotalGameTime + SoundManager.Sounds["collapse"].Duration - TimeSpan.FromMilliseconds(1000);

            if (playerIsFrozen && (gameTime.TotalGameTime > canMoveAgainAt))
            {
                MusicManager.Play("cave1");
                lightsOff = true;
                playerIsFrozen = false;
            }

            if (!buddiTaken && lightsOff && PlayerGrabbedBuddi())
            {
                SoundManager.Play("welcometobuddi");
                buddiTaken = true;
                arrow.Visible = false;
            }

            GamePadState gs = GamePad.GetState(InputManager.PlayerIndex);
            KeyboardState ks = Keyboard.GetState();

            if (!playerIsFrozen)
            {
                CheckForPlayerMovement(gameTime, gs, ks);
            }
            UpdateLampDirection(gs, ks);
            player.SuccumbToGravity();
            player.Update(gameTime);

            CheckForCollisions(gameTime);
            CheckForTriggeredTriggers();

            arrow.Update(gameTime);

            ClampPlayerOntoMap();

            lastPad = gs;
            lastKeyboard = ks;
            base.UpdateScene(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 void UpdateFakeBuddiPosition(GameTime gameTime)
        {
            int moved = GetBuddiMovementFactor(gameTime);

            if (moved > fakeBuddipathLength - 1)
                fakeBuddiOffset.Y = fakeBuddipathLength - (moved - fakeBuddipathLength);
            else
                fakeBuddiOffset.Y = moved;
        }

        private int GetBuddiMovementFactor(GameTime gameTime)
        {
            double temp = gameTime.TotalGameTime.TotalMilliseconds % (fakeBuddipathSpeed * (fakeBuddipathLength * 2));

            return (int)(temp / fakeBuddipathSpeed);
        }

        private bool PlayerGrabbedBuddi()
        {
            return unusedBuddiLocation.Intersects(player.Bounds);
        }

        private void StartCollapse()
        {
            MusicManager.StopAllMusic();
            SoundManager.Play("collapse");
            playerIsFrozen = true;
        }

        private void CheckForTriggeredTriggers()
        {
            for (int i = 0; i < triggers.Count; i++)
            {
                if (player.Bounds.Intersects(triggers[i].Bounds))
                {
                    triggers[i].Activate();
                    triggers.RemoveAt(i);
                }
            }
        }

        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))
            {
                bool cancelJump = false;

                if (player.Bounds.Intersects(exit.Bounds))
                {
                    if (buddiTaken)
                    {
                        SoundManager.Play("leveldone");
                        SceneManager.TransitionTo(new Level19Scene());
                        return;
                    }
                    else
                    {
                        cancelJump = true;
                        SoundManager.Play("doorsaysno");
                        arrow.Visible = true;
                    }
                }

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

        }

        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;
        }


        private void CheckForCollisions(GameTime gameTime)
        {
            foreach (Platform plat in platforms)
            {
                if (plat.Bounds.Intersects(player.Bounds))
                {
                    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["lockerroombg"], Vector2.Zero, Color.White);

            exit.DrawDoor(spriteBatch);

            DrawAllPlatforms(spriteBatch);

            if (lightsOff)
                DrawLampOverlay(spriteBatch);

            exit.DrawSign(spriteBatch);

            if (buddiTaken)
            {
                // draw fake buddi behind player
                Vector2 buddiLoc = player.Location + fakeBuddiLocation + fakeBuddiOffset;
                spriteBatch.Draw(TextureManager.Textures["buddi"], buddiLoc, Color.White);
            }
            else
            {
                // drop prop buddi
                Vector2 buddiLoc = new Vector2(unusedBuddiLocation.Min.X, unusedBuddiLocation.Min.Y);
                spriteBatch.Draw(TextureManager.Textures["buddi"], buddiLoc, Color.White);
            }

            player.Draw(spriteBatch, gameTime);

            arrow.Draw(spriteBatch, gameTime);

            //DrawCollisionTriggers(spriteBatch);

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

            spriteBatch.End();

            base.DrawScene(spriteBatch, gameTime);
        }

        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 DrawCollisionTriggers(SpriteBatch spriteBatch)
        {
            foreach (CollisionTrigger trigger in triggers)
            {
                trigger.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);
            }
        }
    }
}
