using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace LunaLightXMG
{
    public class Player
    {
        private float hsp;                  // Horizontal speed
        public float vsp;                   // Vertical speed
        private float hsp_frac;             // Hold fractional position data
        private float vsp_frac;             // Hold fractional position data
        private float wallJumpDelay;        // Delay for wall jumping
        public bool grounded;               // Is the player grounded?
        public bool walled;                 // Is the player wall sliding?
        private bool wasWalled;             // Remember is the player was on a wall or ground.
        private float hspAcc;               // Horizontal acceleration
        private float hspFrictionGround;    // Ground friction
        private float hspFrictionAir;       // Air friction
        private float hspWalk;              // Maximum horizontal speed
        private float grv;                  // Gravity
        private float vspMax;               // Maximum vertical speed
        private float grvWall;              // Gravity when wall sliding
        private float vspMaxWall;           // Maximum vertical speed when wall sliding
        private float vspJump;              // Jump speed
        private Texture2D texture;
        public Vector2 position;
        public BoundingBox boundingBox { get; private set; }
        private CollisionManager collisionManager;
        
        public Player(Vector2 initPosition)
        {
            hsp = 0;
            vsp = 0;
            hsp_frac = 0;
            vsp_frac = 0;
            wallJumpDelay = 0;
            grounded = false;
            walled = false;
            wasWalled = false;
            hspAcc = 0.2f;
            hspFrictionGround = 0.3f;
            hspFrictionAir = 0.3f;
            hspWalk = 3f;
            grv = 0.2f;
            vspMax = 10f;
            grvWall = 0.1f;
            vspMaxWall = 5f;
            vspJump = -4f;
            position = initPosition;
            boundingBox = new BoundingBox(new Rectangle((int)position.X, (int)position.Y, 8, 16));
            collisionManager = new CollisionManager();
        }

        public void LoadContent(ContentManager content)
        {
            texture = content.Load<Texture2D>("player");
        }

        public void Update(GameTime gameTime, PlayerInput input, Platform[] platforms)
        {
            Movement(input);
            boundingBox.Update(position, boundingBox.Bounds.Width, boundingBox.Bounds.Height);
            DumpFractions(); 
            CollisionChecks(platforms);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            // Snap position to integer values
            Vector2 drawPosition = new Vector2((int)position.X, (int)position.Y);
            spriteBatch.Draw(texture, drawPosition, Color.White);
        }

        // Method Implementation -------------------------------------------------------------------------------
        public void Reset(Vector2 initialPosition)
        {
            position = initialPosition;
            // Reset other player-specific states later
        }

        private void Movement(PlayerInput input)
        {
            if (input.KeyJump && grounded)
            {
                vsp = vspJump;
            }

            // Horizontal movement with acceleration + wall jump delay
            wallJumpDelay = Math.Max(wallJumpDelay - 1, 0); // counts down every frame

            if (wallJumpDelay == 0) // no left-right control until delay is 0, prevents rapid wall climb
            {
                float moveH = input.KeyRight - input.KeyLeft;
                hsp += moveH * hspAcc;

                if (moveH == 0)
                {
                    float hspFrictionFinal = grounded ? hspFrictionGround : hspFrictionAir;
                    hsp = Approach(hsp, 0, hspFrictionFinal);
                }

                hsp = MathHelper.Clamp(hsp, -hspWalk, hspWalk);
            }

            // Calculate vertical movement
            float grvFinal = grv;
            float vspMaxFinal = vspMax;

            if (walled && vsp > 0)
            {
                grvFinal = grvWall;
                vspMaxFinal = vspMaxWall;
            }
            vsp += vsp == 0 ? grvFinal * 2 : grvFinal;
            vsp = MathHelper.Clamp(vsp, vspJump, vspMaxFinal);
        }

        private void CollisionChecks(Platform[] platforms)
        {
            // Horizontal collision prediction
            float onePixh = Math.Sign(hsp);
            if (CollisionManager.IsColliding(position + new Vector2(hsp, 0), boundingBox, platforms))
            {
                while (!CollisionManager.IsColliding(position + new Vector2(onePixh, 0), boundingBox, platforms))
                {
                    position.X += onePixh;
                }
                hsp = 0;
            }
            position.X += hsp; // Horizontal move

            // Vertical collision prediction
            float onePixv = Math.Sign(vsp);
            if (CollisionManager.IsColliding(position + new Vector2(0, vsp), boundingBox, platforms))
            {
                while (!CollisionManager.IsColliding(position + new Vector2(0, onePixv), boundingBox, platforms))
                {
                    position.Y += onePixv;
                }
                vsp = 0;
            }
            position.Y += vsp; // Vertical move

            // Grounded?
            grounded = CollisionManager.IsColliding(position + new Vector2(0, 1), boundingBox, platforms);

            // Walled?
            walled = CollisionManager.WallCheck(position, boundingBox, platforms);
            if (walled)
                wasWalled = walled;
        }

        private float Approach(float start, float end, float shift)
        {
            if (start < end)
            {
                start += shift;
                if (start > end)
                    return end;
            }
            else
            {
                start -= shift;
                if (start < end)
                    return end;
            }
            return start;
        }

        private void DumpFractions()
        {
            if (grounded)
            {
                float adjustedPosX = (float)Math.Round(position.X);
                float adjustedPosY = (float)Math.Round(position.Y);
                position.X = adjustedPosX;
                position.Y = adjustedPosY;
            }

            if (vsp >= 0)
            {
                hsp += hsp_frac;
                vsp += vsp_frac;
                hsp_frac = hsp - (float)Math.Floor(hsp);
                vsp_frac = vsp - (float)Math.Floor(vsp);
                hsp -= hsp_frac;
                vsp -= vsp_frac;
            }
        }
    }
}
