using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace MonoGame.FourEngine
{
    public sealed class FourEngineGame : Game, IFourEngineGame
    {
        private GraphicsDeviceManager _graphics;

        public static FourEngineGame Current { get; private set; }

        public static Assembly[] DefaultAssemblies { get; set; }

        public DrawingSurface RootSurface { get; private set; }
        
        public int TargetFps
        {
            get => _targetFps;
            set
            {
                _targetFps = value;
                OnTargetFps();
            }
        }
        private int _targetFps;

        public Point Resolution
        {
            get => _resolution;
            set
            {
                _resolution = value;
                OnResolutionChanged();
            }
        }
        private Point _resolution;

        public bool FullScreen
        {
            get => _fullscreen;
            set
            {
                _fullscreen = value;
                OnFullScreenChanged();
            }
        }
        private bool _fullscreen;

        public FourEngineGame() : this(null)
        {
        }

        internal FourEngineGame(params Assembly[] additionalAssemblies)
        {
            Current = this;
            _graphics = new GraphicsDeviceManager(this)
            {
                SynchronizeWithVerticalRetrace = true
            };
            
            var assemblies = new List<Assembly>();
            if (DefaultAssemblies != null)
                assemblies.AddRange(DefaultAssemblies);
            if (additionalAssemblies != null)
                assemblies.AddRange(additionalAssemblies);
            assemblies.Add(typeof(FourEngineGame).GetTypeInfo().Assembly);

            ServiceLocator.Init(assemblies.ToArray());
        }

        protected override void Initialize()
        {
            base.Initialize();

            RootSurface = new DrawingSurface(null);
        }

        public List<Scene> GetScenes(Scene root = null)
        {
            if (RootSurface == null)
                return new List<Scene>();
            if (root == null)
                root = RootSurface.Scene;
            var scenes = root.Entities
                .Where(x => x is DrawingSurfaceBase)
                .Cast<DrawingSurfaceBase>()
                .Select(x => x.Scene).ToList();
            foreach (var scene in scenes.ToList())
            {
                scenes.AddRange(GetScenes(scene));
            }
            scenes.Insert(0, root);
            return scenes.Distinct().ToList();
        }

        protected override void Update(GameTime time)
        {
            base.Update(time);

            var keyboard = ServiceLocator.KeyboardControl;
            keyboard.Update(time.ElapsedGameTime);
            var pointer = ServiceLocator.PointerControl;
            pointer.Update(time.ElapsedGameTime);
            var gamePad = ServiceLocator.GamePadControl;
            gamePad.Update(time.ElapsedGameTime);

            RootSurface?.Update(time);
        }
        
        protected override void Draw(GameTime time)
        {
            base.Draw(time);
            RootSurface?.Draw(time);
        }

        private void OnTargetFps() 
            => TargetElapsedTime = TimeSpan.FromMilliseconds(Math.Floor(1000d / TargetFps));

        private void OnResolutionChanged()
        {
            _graphics.PreferredBackBufferWidth = Resolution.X;
            _graphics.PreferredBackBufferHeight = Resolution.Y;
            _graphics.ApplyChanges();
        }

        private void OnFullScreenChanged()
        {
            _graphics.IsFullScreen = FullScreen;
            _graphics.ApplyChanges();
        }
    }
}