using Igdc.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;

namespace Igdc.Scenes
{
    public abstract class SceneBase : IDisposable
    {
        public const int TargetWidth = 160;
        public const int TargetHeight = 144;

        public SceneBase Parent { get; set; }

        public virtual int ButtonTimeout => 10;

        public abstract void Update(GameTime time);
        public abstract void Draw(GameTime time);
        public abstract void Dispose();
    }

    public abstract class SceneBase<T> : SceneBase
    {
        private SceneIterationArgument _arg;

        private RenderTarget2D _render;

        public SceneBase(GraphicsDeviceManager manager, GraphicsDevice device, T package)
        {
            var screenSize = new Point(TargetWidth, TargetHeight);
            _render = new RenderTarget2D(device, screenSize.X, screenSize.Y, false, device.PresentationParameters.BackBufferFormat, DepthFormat.Depth24);
            var batch = new SpriteBatch(device);
            // TODO: Replace with SMTH better
            var control = GamePad.GetCapabilities(PlayerIndex.One).IsConnected 
                ? (ControlConfigBase)new GamepadConfig()
                : new KeyboardConfigSingleplayer();
            _arg = new SceneIterationArgument(manager, device, batch, control, screenSize);
        }

        public override void Update(GameTime time)
        {
            var e = _arg;
            e.GameTime = time;
            e.Control.Refresh();
            if (e.Control.RestartButton.Released()) IgdcGame.Current.Load();
            if (e.Control.FullscreenButton.Released())
            {
                e.Manager.IsFullScreen = !e.Manager.IsFullScreen;
                if (e.Manager.IsFullScreen)
                {
                    e.Manager.PreferredBackBufferWidth = e.Device.Adapter.CurrentDisplayMode.Width;
                    e.Manager.PreferredBackBufferHeight = e.Device.Adapter.CurrentDisplayMode.Height;
                }
                else
                {
                    e.Manager.PreferredBackBufferWidth = 800;
                    e.Manager.PreferredBackBufferHeight = 480;
                }
                e.Manager.ApplyChanges();
            }
            if (e.Control.ExitButton.Released())
                IgdcGame.Current.Exit();
            Update(e);
        }

        public override void Draw(GameTime time)
        {
            var e = _arg;
            e.GameTime = time;
            e.Device.SetRenderTarget(_render);
            e.Batch.Begin();
            Draw(e);
            if (IgdcGame.Current.Transition > 0)
                e.Batch.Draw(IgdcGame.Current.FlatTexture, new Rectangle(0, 0, 160 - IgdcGame.Current.Transition, TargetHeight), Palette.Dark);
            e.Batch.End();
            var ratioX = e.Manager.PreferredBackBufferWidth / (double)e.ScreenSize.X;
            var ratioY = e.Manager.PreferredBackBufferHeight / (double)e.ScreenSize.Y;
            var ratio = ratioX < ratioY ? ratioX : ratioY;
            var width = (int)(e.ScreenSize.X * ratio);
            var height = (int)(e.ScreenSize.Y * ratio);
            var offsetX = ratioX < ratioY ? 0 : (e.Manager.PreferredBackBufferWidth - width) / 2;
            var offsetY = ratioY < ratioX ? 0 : (e.Manager.PreferredBackBufferHeight - height) / 2;
            e.Device.SetRenderTarget(null);

            var vibrate = (int)((e.Control.VibrateLeft + e.Control.VibrateRight) * (e.Manager.PreferredBackBufferHeight / 10));

            e.Device.Clear(Palette.Dark);
            e.Batch.Begin(samplerState: SamplerState.PointClamp);
            e.Batch.Draw(_render, new Rectangle(
                offsetX + (vibrate > 0 ? e.Random.Next(vibrate) : 0),
                offsetY + (vibrate > 0 ? e.Random.Next(vibrate) : 0),
                width, height), 
                Color.White);
            e.Batch.End();
        }

        protected abstract void Update(SceneIterationArgument e);
        protected abstract void Draw(SceneIterationArgument e);
    }

    public class SceneIterationArgument
    {
        public Random Random { get; } = new Random(DateTime.UtcNow.Millisecond);
        public GraphicsDeviceManager Manager { get; }
        public GraphicsDevice Device { get; }
        public SpriteBatch Batch { get; }
        public ControlConfigBase Control { get; }
        public Point ScreenSize { get; }
        public GameTime GameTime { get; set; }

        public SceneIterationArgument(GraphicsDeviceManager manager, GraphicsDevice device, SpriteBatch batch, ControlConfigBase control, Point screenSize)
        {
            Manager = manager;
            Device = device;
            Batch = batch;
            Control = control;
            ScreenSize = screenSize;
        }
    }
}