using System;
using System.Text;
using System.Collections.Generic;

using PlanetExpress.Tools;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using System.Runtime.InteropServices;

using System.Drawing;

namespace PlanetExpress {	
	
	public class GameApp : GameWindow {		
		
		public static int ScreenWidth = 800;
		public static int ScreenHeight = 600;
		
		[STAThread]
		private static void Main() {
			//if (Environment.OSVersion.Platform == PlatformID.Win32Windows)
				InitSnd();
			GameApp main = new GameApp();
			
			FStars = new BackgroundStarsClass();			
			FFontRenderBig = new FontRenderClass(
			                                  new Font(FontFamily.GenericSansSerif, 28, FontStyle.Bold),
			                                  System.Text.Encoding.GetEncoding(1251));
			FFontRender = new FontRenderClass(
			                                  new Font(FontFamily.GenericSerif, 14),
			                                  System.Text.Encoding.GetEncoding(1251));
			
			main.Run();
		}
				
		public GameApp() : base(ScreenWidth, ScreenHeight, GraphicsMode.Default, "Planet Express") {
			VSync = VSyncMode.On;			
			this.WindowBorder = WindowBorder.Fixed;
			SwichToScreenEventArgsHandler(
			                              this,
			                              new SwichToScreenEventArgs(
			                                                         new NewMissionScreenClass("lvl1")
			                                                         )
			                              );
			
		}		
		
		protected override void OnUpdateFrame (OpenTK.FrameEventArgs e) {
			//ActiveScreen.Action(e.Time);
		}

		
		object render_state_lock = new object();
		bool render_state = false;
		protected override void OnRenderFrame (OpenTK.FrameEventArgs e) {						
			lock (render_state_lock) {
				if (render_state) return;
				render_state = true;
			}
			try {
				VSync = VSyncMode.On;
				
				SwithToNextScreen();
				//ActiveScreen.Action(e.Time);
				
				GL.Enable(EnableCap.DepthTest);
				GL.DepthFunc(DepthFunction.Lequal);
				
				GL.Enable(EnableCap.Texture2D);
				
				GL.Enable(EnableCap.AlphaTest);
				GL.AlphaFunc(AlphaFunction.Gequal, 0.1f);
				
				GL.Enable(EnableCap.Blend);				
				GL.BlendFunc(BlendingFactorSrc.SrcAlpha,
				             BlendingFactorDest.OneMinusSrcAlpha);
				
				if (ActiveScreen != null) {
					ActiveScreen.Draw();
					ActiveScreen.Action(e.Time);
				}
							
				SwapBuffers();
			} catch (Exception exc) {
				Console.WriteLine(exc.ToString());
			} finally {
				lock (render_state_lock) {
					render_state = false;
				}			
			}	
		}

		protected override void OnResize (System.EventArgs e){
			base.OnResize (e);
			GL.Viewport(0, 0, ClientSize.Width, ClientSize.Height);
			#region матрица для ортогональной проекции
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadIdentity();
			GL.Ortho(-1, -1, 1, 1, -1, 1);
			GL.Translate(-1, 1, 0);
			GL.Scale(2.0/ClientSize.Width, -2.0/ClientSize.Height, 1);			
			#endregion
			GL.MatrixMode(MatrixMode.Modelview);
		} 				
		
		private CustomGameScreenClass ActiveScreen = null;
		private CustomGameScreenClass NextScreen = null;
		private void SwichToScreenEventArgsHandler(object sender,
		                                           SwichToScreenEventArgs e) {
			lock (this) {
				NextScreen = e.Screen;
			}
		}
		
		private void SwithToNextScreen() {
			lock (this) {
				if (NextScreen == null) return;
				if (ActiveScreen != null) {
					Mouse.Move -= ActiveScreen.MouseMove;
					Mouse.ButtonDown -= ActiveScreen.MouseDown;
					Mouse.ButtonUp -= ActiveScreen.MouseUp;
					Mouse.WheelChanged -= ActiveScreen.MouseWheel;
					Keyboard.KeyDown -= ActiveScreen.KeyDown;
					Keyboard.KeyUp -= ActiveScreen.KeyUp;
				}
				ActiveScreen = NextScreen;
				if (ActiveScreen != null) {
					Mouse.Move += ActiveScreen.MouseMove;
					Mouse.ButtonDown += ActiveScreen.MouseDown;
					Mouse.ButtonUp += ActiveScreen.MouseUp;
					Mouse.WheelChanged += ActiveScreen.MouseWheel;
					Keyboard.KeyDown += ActiveScreen.KeyDown;
					Keyboard.KeyUp += ActiveScreen.KeyUp;
				}
				ActiveScreen.SwithToScreen += SwichToScreenEventArgsHandler;
				NextScreen = null;
			}			
		}
		
		//GAMEAPP
		public static string GameFileName(params string[] args) {
			return String.Join(
			                   System.IO.Path.DirectorySeparatorChar.ToString(),
			                   args);
		}		
		
		static bool PlaySnd;
		private static void InitSnd() {
			PlaySnd = true;
		}
		
		[DllImport("winmm.dll")]
		private static extern long mciSendString(string strCommand, StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);
				
		public class SndFrame {						
			public static Dictionary<string, SndFrame> SndFrames =
				new Dictionary<string, SndFrame>();
			
			public static void Play(string sndName) {
				SndFrame fm;
				if (SndFrames.TryGetValue(sndName, out fm)) {
					fm.Play();
				} else {
					fm = new SndFrame(sndName);
					SndFrames.Add(sndName, fm);
					fm.Play();
				}
			}
			
			static int SndLastFrame = 0;
			int StartFrame;
			int Frame = 0;						
			public SndFrame(string sndName) {
				StartFrame = SndLastFrame;
				SndLastFrame += 3;
				for (int i=StartFrame; i<StartFrame+3; i++) {
					mciSendString(
			              String.Format("open \"{0}\" type mpegvideo alias snd{1}", sndName, i),
			              null,
			              0,
			              IntPtr.Zero);	
					
				}				
			}
			
			void Play() {
				mciSendString(
				          String.Format("play snd{0}",
				                            StartFrame + Frame),
			              null,
			              0,
			              IntPtr.Zero);
				Frame++;
				if (Frame >2) Frame = 0;
			}
		}			
		
		public static void SndPlay(string sndName) {
			if (!PlaySnd) return;
			sndName = GameApp.GameFileName("data", "sounds", sndName);			
			SndFrame.Play(sndName);
		}
				
		
		public static void SndPlayLoop(string sndName) {
			if (!PlaySnd) return;
			sndName = GameApp.GameFileName("data", "sounds", sndName);
			mciSendString(
			              String.Format("open \"{0}\" type mpegvideo alias sndLoop", sndName),
			              null,
			              0,
			              IntPtr.Zero);
    		mciSendString("play sndLoop repeat",
			              null,
			              0,
			              IntPtr.Zero);
		}
		
		public static void SndStopLoop() {
			if (!PlaySnd) return;
			mciSendString("stop sndLoop",
			              null,
			              0,
			              IntPtr.Zero);
		}
		
		private static BackgroundStarsClass FStars;
		public static BackgroundStarsClass Stars {
			get { return FStars; }		
		}
		
		private static FontRenderClass FFontRenderBig;
		public static FontRenderClass FontRenderBig {
			get { return FFontRenderBig; }
		}
		private static FontRenderClass FFontRender;
		public static FontRenderClass FontRender {
			get { return FFontRender; }
		}
	}
}