using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.IO;
using Microsoft.Xna.Framework.Input;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Audio;

namespace LunaLightXMG
{
    public class Level : Game, IDisposable
    {
        // Objects
        private Camera2D camera2D;
        private PlayerInput playerInput;

        // Native retro scale
        private int retroWidth = 320;
        private int retroHeight = 180;

        // Entities
        private List<Enemy> enemies;
        private int numberOfEnemies;
        public Player Player
        {
            get { return player; }
        }
        Player player;

        // Key Locations
        private Vector2 startPosition;
        private Point exit = InvalidPosition;
        private static readonly Point InvalidPosition = new Point(-1,-1);

        // Physical Structure
        private Platform[] platforms;
        private int numberOfPlatforms;

        // Game State
        private Random random = new Random(546452);

        public int Score
        {
            get { return score; }
        }
        int score;

        public ContentManager Content
        {
            get { return content; }
        }
        ContentManager content;

        // Music
        private SoundEffect sinkBeats;
        private SoundEffectInstance backgroundMusicInstance;
        
        // Test Render Target Handler
        private RenderHandler renderHandler;
        bool resizing;

        // Debugging
        private SpriteFont debugFont;
        private Texture2D debugGrid;

        // Keyboard input
        private KeyboardState prevKeyState;

        // Utility
        public Utilities utilities;

        // Textures
        Texture2D backgroundTexture;
        Texture2D platformTexture;


        #region Loading

        // Level Definition
        public Level(IServiceProvider serviceProvider,SpriteBatch spriteBatch, RenderHandler render_handler)
        {
            // Create content manager to load content used by just this level.
            content = new ContentManager(serviceProvider, "Content");

            renderHandler = render_handler;

            // Initialize camera
            camera2D = new Camera2D(retroWidth, retroHeight);
            // camera test - set level bounds
            camera2D.SetLevelBounds(new Rectangle(0,0,480,270)); // Testing - default 320x180

            // Initialize Render Handler

            // Load background layer textures. For now, all levels
            // will use the same texture.
            backgroundTexture = Content.Load<Texture2D>("Sink area");
            // Load platform layer textures. For now, all platforms
            // will use the same texture.
            platformTexture = Content.Load<Texture2D>("platform");

            // Load player sprite
            // Test player input and movement with abstracted player class
            Vector2 initPlayerPosition = new Vector2(163, 0); 
            player = new Player(initPlayerPosition);
            playerInput = new PlayerInput();
            playerInput.HasControl = true;
            player.LoadContent(Content);

            // Init number of platforms / enemies
            numberOfPlatforms = 4;
            numberOfEnemies = 10;

            // Load enemies
            enemies = SpawnEnemies(numberOfEnemies);

            

            // Load Platforms
            LoadPlatforms();

            // Debugging
            debugFont = Content.Load<SpriteFont>("fonts/debugFont");
            debugGrid = Content.Load<Texture2D>("debugGrid");

            // Get utilities
            utilities = new Utilities();

            // Misc.
            prevKeyState = Keyboard.GetState();
        }

        private void LoadPlatforms()
        {
            platforms = PlatformGenerator.CreatePlatforms(numberOfPlatforms);
        }

        private List<Enemy> SpawnEnemies(int numOfEnemies)
        {
            List<Enemy> newEnemies = new List<Enemy>();
            for (int i = 0; i < numOfEnemies; i++)
            {
                Vector2 spawnPosition = new Vector2(16 + i * 3, -16);
                Enemy enemy = new(spawnPosition, random.Next(1,3));
                enemy.LoadContent(Content);
                newEnemies.Add(enemy);
            }
            return newEnemies;
        }

        #endregion

        #region Update

        public void Update(GameTime gameTime, KeyboardState keyboardState)
        {
            // Temporary quit game
            if (keyboardState.IsKeyDown(Keys.Escape))
                Exit();
            // Check for game reset
            if (keyboardState.IsKeyDown(Keys.R))
                ResetGame();
            
            // Update player
            playerInput.Update(keyboardState);
            player.Update(gameTime, playerInput, platforms);

            // Update Enemies
            foreach(var enemy in enemies)
                enemy.Update(gameTime, player, platforms);
        
            // Test render handler 
            renderHandler.UpdateZoom();

            //Update camera position
            camera2D.Follow(gameTime, player.position);
            camera2D.UpdateScreenShake(gameTime);

            // Camera testing
            TestCameraWhilePlaying();
            ToggleBackgroundMusic();
        
            // Update keyState
            prevKeyState = Keyboard.GetState();
        }
        
        public void Dispose()
        {
            Content.Unload();
        }

        #endregion

        #region Draw

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
             // // Test Render Target Handler - Draw sprites to the render (target
            renderHandler.BeginRenderTarget(camera2D.GetViewMatrix());
            
            // Test draw tiles
            foreach (var platform in platforms)
            {
                platform.Draw(spriteBatch, platformTexture); // Assuming all tiles use the same texture for now
            }
            
            // Test player input and movement with abstracted player class
            player.Draw(spriteBatch);
            
            foreach (var enemy in enemies)
            {
                enemy.Draw(spriteBatch);
            }
            
            renderHandler.EndRenderTarget();
            
            renderHandler.DrawRenderTarget(player, debugFont, camera2D);

            
        }

        #endregion

        #region Methods

        private void PlayBackgroundMusic(SoundEffect backgroundMusic)
        {
            if (backgroundMusicInstance == null)
            {
                backgroundMusicInstance = backgroundMusic.CreateInstance();
                backgroundMusicInstance.IsLooped = true;
            }
            if (backgroundMusicInstance.State == SoundState.Playing)
            {
                backgroundMusicInstance.Stop();
            }
            else
            {
                backgroundMusicInstance.Play();
            }
        }

        public void ResetGame()
        {
            // reset cam
            camera2D.Rotation = 0;
            camera2D.Zoom = 1f;

            // reset render target zoom
            renderHandler.ZoomNormalize();
            
            // Reset player position
            player.Reset(new Vector2(163, 0));

            // Clear and respawn enemies
            enemies.Clear();
            SpawnEnemies(numberOfEnemies);

            // Regenerate platforms
            LoadPlatforms();
        }

        

        #endregion

        #region Testing

        private void DrawDebugInfo(SpriteBatch spriteBatch)
        {
            // Draw debug messages
            spriteBatch.DrawString(debugFont, $"VSP: {player.vsp}", new Vector2(10, 10), Color.White);
            spriteBatch.DrawString(debugFont, $"Grounded: {player.grounded}", new Vector2(10, 30), Color.White);
            spriteBatch.DrawString(debugFont, $"Walled: {player.walled}", new Vector2(10, 50), Color.White);
            spriteBatch.DrawString(debugFont, $"Position: {player.position}", new Vector2(10, 70), Color.White);
            spriteBatch.DrawString(debugFont, $"Camera Position: {camera2D.Position}", new Vector2(10, 90), Color.White);
            spriteBatch.DrawString(debugFont, $"Camera Rotation: {camera2D.Rotation}", new Vector2(10, 110), Color.White);
            spriteBatch.DrawString(debugFont, $"Render Target Zoom: {renderHandler.ZoomLevel}", new Vector2(10, 130), Color.White);

            spriteBatch.DrawString(debugFont, $"Camera Rotation Control - Rotate left: K, Rotate right: L", new Vector2(10, 190), Color.White);
            spriteBatch.DrawString(debugFont, $"Render Target Zoom Control - Zoom in: U, Zoom normal: I, Zoom out: O", new Vector2(10, 210), Color.White);
            spriteBatch.DrawString(debugFont, $"Press P to test screen shake.", new Vector2(10, 230), Color.White);
            spriteBatch.DrawString(debugFont, $"Press M to toggle game music.", new Vector2(10, 250), Color.White);
            spriteBatch.DrawString(debugFont, $"Press R to reset level.", new Vector2(10, 270), Color.White);
        }

        private void ToggleBackgroundMusic()
        {
            KeyboardState currKeyState = Keyboard.GetState();
            if (prevKeyState.IsKeyUp(Keys.M) && currKeyState.IsKeyDown(Keys.M))
            {
                PlayBackgroundMusic(sinkBeats);
            }
        }
        private void TestCameraWhilePlaying()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            var KeyK = keyboardState.IsKeyDown(Keys.K);
            var KeyL = keyboardState.IsKeyDown(Keys.L);
            var KeyU = keyboardState.IsKeyDown(Keys.U);
            var KeyI = keyboardState.IsKeyDown(Keys.I);
            var KeyO = keyboardState.IsKeyDown(Keys.O);
            var KeyP = keyboardState.IsKeyDown(Keys.P);
            if (KeyK)
            {
                camera2D.Rotation -= 0.01f;
            }
            if (KeyL)
            {
                camera2D.Rotation += 0.01f;
            }
            if (KeyU)
            {
                renderHandler.ZoomIn();
            }
            if (KeyI)
            {
                renderHandler.ZoomNormalize();
            }
            if (KeyO)
            {
                renderHandler.ZoomOut();
            }
            if (KeyP)
            {
                camera2D.ScreenShake(0.5f, 5f);
            }
        }

        #endregion
    }
}