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

namespace LunaLightXMG
{
    public class Camera2D
    {
        // Fields
        private readonly Viewport viewport;
        private float zoom;
        private float rotation;
        private Vector2 position;
        private Rectangle levelBounds;
        private readonly float camSpeed = 5.0f;

        // Screen Shake
        private float shakeTimeRemaining = 0f;
        private float shakeMagnitudeCurrent = 0f;
        private bool shaking = false;

        // Util
        readonly Utilities util;
        
        // Getters and setters
        public Vector2 Position
        {
            get => position;
            set => position = value;
        }
        public float Zoom
        {
            get => zoom;
            set => zoom = (int)Math.Floor(MathHelper.Clamp(value, 1f, 2f));
        }
        public float Rotation
        {
            get => rotation;
            set => rotation = value;
        }
        
        // constructor
        public Camera2D(int width, int height)
        {
            viewport = new Viewport(0, 0, width, height);
            levelBounds = new Rectangle(0, 0, 320, 180); // Default
            zoom = 1f;
            rotation = 0f;
            position = Vector2.Zero;
            util = new Utilities();
        }
        
        public Matrix GetViewMatrix()
        {
            return
                Matrix.CreateTranslation(new Vector3(-position, 0f)) *
                Matrix.CreateRotationZ(rotation) *
                Matrix.CreateScale(zoom, zoom, 1f) *
                Matrix.CreateTranslation(new Vector3(viewport.Width * 0.5f, viewport.Height * 0.5f, 0f));
        }

        public void SetLevelBounds(Rectangle bounds)
        {
            levelBounds = bounds;
        }

        public void Follow(GameTime gameTime, Vector2 targetPostion)
        {
            float smoothSpeed = camSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            position = Vector2.Lerp(position, targetPostion, smoothSpeed);
            if (Vector2.Distance(position, targetPostion) < 1.0f)
            {
                position = targetPostion;
            }
            ClampToBounds();
        }
        
        public void ScreenShake(float shakeTime, float shakeMagnitude)
        {
            shakeTimeRemaining = shakeTime;
            shakeMagnitudeCurrent = shakeMagnitude;
            shaking = true;
        }
        
        public void UpdateScreenShake(GameTime gameTime)
        {
            if (!shaking) return;

            if (shakeTimeRemaining > 0)
            {
                float shakeX = util.RandomFromRange(-shakeMagnitudeCurrent, shakeMagnitudeCurrent);
                float shakeY = util.RandomFromRange(-shakeMagnitudeCurrent, shakeMagnitudeCurrent);
                position.X += shakeX;
                position.Y += shakeY;
                shakeTimeRemaining -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                shakeMagnitudeCurrent *= 0.9f;
            }
            else
            {
                shaking = false;
                shakeTimeRemaining = 0f;
                shakeMagnitudeCurrent = 0f;
            }
        }

        private void ClampToBounds()
        {
            // Calculate the cam's half viewport size
            var halfViewportWidth = viewport.Width * 0.5f / zoom;
            var halfViewportHeight = viewport.Height * 0.5f / zoom;
            
            // Clamp cam to level bounds
            float minX = levelBounds.Left + halfViewportWidth;
            float maxX = levelBounds.Right - halfViewportWidth;
            float minY = levelBounds.Top + halfViewportHeight;
            float maxY = levelBounds.Bottom - halfViewportHeight;

            // Set cam position within level bounds
            position.X = MathHelper.Clamp(position.X, minX, maxX);
            position.Y = MathHelper.Clamp(position.Y, minY, maxY);
        }
    }
}