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

using System;
using System.Threading;
using System.Diagnostics;

using EVE.EVEScene;
using EVE.EVEShaders;
using EVE.EVECameras;
using EVE.EVELights;
using EVE.EVEOctrees;
using EVE.EVEAssets;
using EVE.EVEGpgpu;
using EVE.EVEThreading;
using EVE.EVERenderer;
using EVE.EVELogic;

namespace EVE {
    public delegate void UpdateFrameHandler();

	public class EVEGame : Game {
		private GraphicsDeviceManager myGraphics;
		private ContentManager myContent;
		private EVEInput myInput;

        private static double myTime;

        private bool firstRun;

        public double Time { get { return myTime; } }

		public GraphicsDeviceManager Graphics { get { return myGraphics; } }
		public EVEInput Input { get { return myInput; } }

        public ContentManager Content { get { return myContent; } }

        public UpdateFrameHandler OnUpdate;

        public EVENavierStokes NavierStokes = EVE.EVEGpgpu.EVENavierStokes.Instance;

        SpriteBatch spriteBatch;
        SpriteFont font;

        private EVEDoubleBuffer doubleBuffer;
        private EVEDefaultRenderer defaultRenderer;
        private EVEDefaultUpdater defaultUpdater;

        public EVEDefaultRenderer Renderer { get { return defaultRenderer; } }
        public EVEDefaultUpdater Updater { get { return defaultUpdater; } }

        Random rand = new Random();
        Stopwatch watch = new Stopwatch();
        double totalFrameMs = 0;
        double drawFrameMs = 0;
        double updateFrameMs = 0;
        
		public EVEGame() {
			myGraphics = new GraphicsDeviceManager(this);
			myContent = new ContentManager(Services);
			myInput = new EVEInput(this);

            IsFixedTimeStep = false;
            myGraphics.SynchronizeWithVerticalRetrace = false;

            firstRun = true;

			this.Components.Add(myInput);

            doubleBuffer = new EVEDoubleBuffer();
            defaultRenderer = new EVEDefaultRenderer(doubleBuffer, this);
            defaultUpdater =  new EVEDefaultUpdater(doubleBuffer, this);

            EVEGlobalManager.prepareManager(defaultUpdater, defaultRenderer);

			Window.AllowUserResizing = true;
			Window.ClientSizeChanged += new System.EventHandler(Window_ClientSizeChanged);
		}

		void Window_ClientSizeChanged(object sender, System.EventArgs e) {
			myGraphics.PreferredBackBufferWidth = Window.ClientBounds.Width;
			myGraphics.PreferredBackBufferHeight = Window.ClientBounds.Height;

			EVECameraManager.UpdateViewports(myGraphics.GraphicsDevice.Viewport);
		}

		protected override void Initialize() {
            NavierStokes.Initialize(myGraphics.GraphicsDevice, myContent);

            EVECamera defaultCamera = new EVECamera(myGraphics.GraphicsDevice.Viewport);
            EVECameraManager.AddCamera(defaultCamera, "default");
            EVECameraManager.SetActiveCamera("default");

            EVELight defaultLight = new EVELight();
            defaultLight.Position = new Vector3(0, 1, 1);
            EVELightManager.AddLight(defaultLight, "default");
            EVELightManager.SetActiveLight("default");

            EVEShaderManager.Initialize();

            EVEGpuSupport.Instance.initialize(myGraphics.GraphicsDevice, myContent);

            base.Initialize();
		}

		protected override void LoadContent() {
            spriteBatch = new SpriteBatch(Graphics.GraphicsDevice);
            font = Content.Load<SpriteFont>(@"Content/font");

            EVEModelManager.LoadContent(myGraphics.GraphicsDevice, myContent);
            EVEShaderManager.LoadContent(myGraphics.GraphicsDevice, myContent);

			Renderer.Scene.LoadGraphicsContent(myGraphics.GraphicsDevice , myContent);

            defaultUpdater.StartOnNewThread();

			base.LoadContent();
		}

        protected override void OnExiting(object sender, EventArgs args)
        {
            if (defaultUpdater.RunningThread != null)
                defaultUpdater.RunningThread.Abort();
        }

		protected override void Draw(GameTime gameTime) {
            
            myGraphics.GraphicsDevice.Clear(Color.Azure);

            watch.Reset();
            watch.Start();

            doubleBuffer.GlobalStartFrame(gameTime);

            //defaultUpdater.DoFrame();

            Renderer.FrameWatch.Reset();
            Renderer.FrameWatch.Start();
            

            Renderer.DoFrame();

			base.Draw(gameTime);

            EVEShaderManager.PostProcess(myGraphics.GraphicsDevice);
            EVEGpuSupport.Instance.computeTask();

            /**
            if (firstRun)
            {
                NavierStokes.PerformArrayInit(new Vector4(0f, 0f, 0f, 0f));
                firstRun = false;
            }

            NavierStokes.PerformFullStep(gameTime);
            NavierStokes.DrawToBuffer(0, null);
            */
            
            spriteBatch.Begin();
            spriteBatch.DrawString(font, "Frame Time (ms): " + totalFrameMs, new Vector2(10, 10), Color.Red);
            spriteBatch.DrawString(font, "Update Thread Time (ms): " + updateFrameMs, new Vector2(10, 30), Color.Red);
            spriteBatch.DrawString(font, "Render Thread Time (ms): " + drawFrameMs, new Vector2(10, 50), Color.Red);
            spriteBatch.DrawString(font, "Buffer Load : " + doubleBuffer.ChangeMessageCount, new Vector2(10, 70), Color.Red);
            spriteBatch.End();
            
		}

        protected override void EndDraw()
        {
            base.EndDraw();
            Renderer.FrameWatch.Stop();
            doubleBuffer.GlobalSynchronize();
            watch.Stop();

            totalFrameMs = watch.Elapsed.TotalMilliseconds;
            drawFrameMs = Renderer.FrameWatch.Elapsed.TotalMilliseconds;
            updateFrameMs = Updater.FrameWatch.Elapsed.TotalMilliseconds;

        }

		protected override void Update(GameTime gameTime) {
            myTime = gameTime.TotalGameTime.TotalSeconds;
            if (OnUpdate != null) OnUpdate();
            
            EVECameraManager.ActiveCamera.Update();
            base.Update(gameTime);
		}
	}
}