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
{
    public enum AnimationStates
    {
        Idle,
        Walking
    }

    class PlayerCharacter
    {
        private Vector2 location;               // positions are for screens, locations are for worlds
        public Vector2 Location { get { return location; } }

        public BoundingBox Bounds
        {
            get
            {
                return new BoundingBox(new Vector3(location.X, location.Y, 0),
                                        new Vector3(location.X + FrameWidth, location.Y + FrameHeight, 0));
            }
        }

        public Vector2 FootLocation { get { return location + new Vector2(0, FrameHeight); } }
        public Vector2 HeadLocation 
        { 
            get 
            { 
                if (mirrorSprite)
                    return location + new Vector2(FrameWidth - 18, 9); 
                else
                    return location + new Vector2(18, 9); 
            } 
        }


        private int FrameWidth = 45;
        private int FrameHeight = 75;

        const int MovementSpeedPixelsPerSecond = 150;
        static Vector2 ClimbSpeed = new Vector2(0, 4);
        static Vector2 Gravity = new Vector2(0f,0.6f);
        static Vector2 JumpImpulse = new Vector2(0, -8);

        Dictionary<AnimationStates, int> numAnimFrames = new Dictionary<AnimationStates, int>();
        Dictionary<AnimationStates, int> animationRows = new Dictionary<AnimationStates, int>();
        Dictionary<AnimationStates, TimeSpan> animationDelay = new Dictionary<AnimationStates, TimeSpan>();

        public bool Falling = false;

        AnimationStates animState = AnimationStates.Idle;
        private TimeSpan nextFrameAt = TimeSpan.MinValue;
        private int currentFrame = 0;
        private bool mirrorSprite = false;
        public bool Mirrored { get { return mirrorSprite; } }
        private Vector2 currentVelocity = Vector2.Zero;

        private bool hasWalked;

        public PlayerCharacter(Vector2 loc)
        {
            numAnimFrames.Add(AnimationStates.Idle, 2);
            numAnimFrames.Add(AnimationStates.Walking, 4);

            animationRows.Add(AnimationStates.Idle, 0);
            animationRows.Add(AnimationStates.Walking, 1);

            animationDelay.Add(AnimationStates.Idle, TimeSpan.FromMilliseconds(400));
            animationDelay.Add(AnimationStates.Walking, TimeSpan.FromMilliseconds(200));

            location = loc;
        }

        public void Update(GameTime gameTime)
        {
            HandleAnimation(gameTime);

            if (gameTime.TotalGameTime > nextFrameAt)
            {
                currentFrame = (++currentFrame) % numAnimFrames[animState];
                nextFrameAt = gameTime.TotalGameTime + animationDelay[animState];
            }

            hasWalked = false;
        }

        public void Ascend()
        {
            location -= ClimbSpeed;
        }

        public void Descend()
        {
            location += ClimbSpeed;
        }

        public void Jump(Vector2 horizontalBoost)
        {
            currentVelocity += JumpImpulse;
            currentVelocity += horizontalBoost;
        }

        public void StopFalling()
        {
            currentVelocity = Vector2.Zero;
            Falling = false;
        }

        public void SuccumbToGravity()
        {
            if (Falling)
            {
                currentVelocity += Gravity;
            }

            location += currentVelocity;
        }

        public void MoveLeft(GameTime gameTime)
        {
            Vector2 moveOffset = Directions.Left;
            moveOffset.X = GetDistanceToMove(gameTime);
            location -= moveOffset;

            if (lastAnimationState != AnimationStates.Walking)
            {
                animState = AnimationStates.Walking;
                nextFrameAt = gameTime.TotalGameTime + animationDelay[animState];
                currentFrame = 0;
            }

            mirrorSprite = true;
            hasWalked = true;
        }

        public void MoveRight(GameTime gameTime)
        {
            Vector2 moveOffset = Directions.Right;
            moveOffset.X = GetDistanceToMove(gameTime);
            location += moveOffset;

            if (lastAnimationState != AnimationStates.Walking)
            {
                animState = AnimationStates.Walking;
                nextFrameAt = gameTime.TotalGameTime + animationDelay[animState];
                currentFrame = 0;
            }

            mirrorSprite = false;
            hasWalked = true;
        }

        AnimationStates lastAnimationState;
        private void HandleAnimation(GameTime gameTime)
        {
            if (!hasWalked && lastAnimationState != AnimationStates.Idle)
                {
                    animState = AnimationStates.Idle;
                    nextFrameAt = gameTime.TotalGameTime + animationDelay[animState];
                    currentFrame = 0;
                }

            lastAnimationState = animState;
        }

        private float GetDistanceToMove(GameTime gameTime)
        {
            double scaleFactor = gameTime.ElapsedGameTime.TotalMilliseconds / 1000;
            double distanceToMove = MovementSpeedPixelsPerSecond * scaleFactor;

            return (float)distanceToMove;
        }

        public void GoTo(int x, int y)
        {
            location = new Vector2(x,y);
        }

        public void StandAt(Vector2 loc)
        {
            location = loc - new Vector2(0, FrameHeight - 2);
        }

        public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
                Rectangle sourceRect = new Rectangle(currentFrame * FrameWidth,
                                                     animationRows[animState] * FrameHeight, 
                                                     FrameWidth, 
                                                     FrameHeight);

            SpriteEffects effects;
            if (mirrorSprite)
                effects = SpriteEffects.FlipHorizontally;
            else
                effects = SpriteEffects.None;

                spriteBatch.Draw(TextureManager.Textures["playersprite"], 
                    new Rectangle((int)location.X, (int)location.Y, FrameWidth, FrameHeight), sourceRect,
                    Color.White, 0, Vector2.Zero, effects, 0);
        }

    }
}
