
using System;
using System.Xml;
using System.Collections.Generic;
using System.Reflection;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;

using PlanetExpress.Tools;
using PlanetExpress.items;
using PlanetExpress.items.effects;
using PlanetExpress.items.enemys;
using PlanetExpress.items.shells;
using PlanetExpress.items.weapon;

using Math2D;
using Math2D.Shapes;

namespace PlanetExpress {	
	
	public enum ScreenSate { game, restart, next};
	
	public class MissionScreenClass : CustomGameScreenClass {
		
		public static MissionScreenClass Area;			
		
		private string[] LevelData;
		private float FlyinLength = -1f;
		private int LevelLine = -1;
		
		
		public PlayerClass Player;
		string NextLevel;
		TexClass Heard;
		TexClass Energy;
		public MissionScreenClass(XmlNode lvl, string next) {
			Init();
			NextLevel = next;
			Area = this;
			Player = new PlayerClass();
			Objects.Add(Player);
			LoadLevel(lvl);
			
			Heard = TexMan.LoadTexture(GameApp.GameFileName("data", "items", "heard.png"));
			Energy = TexMan.LoadTexture(GameApp.GameFileName("data", "items", "energy.png"));
		}		
		
		List<object> InitList = new List<object>();
		void Init() {		
			foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())	{
				if (t.FullName.IndexOf("PlanetExpress.items") != -1) {
					ConstructorInfo ctor = t.GetConstructor(new Type[0]);
					if (ctor != null) {
						InitList.Add(ctor.Invoke(new object[0]));
					}
				}
			}
		}
		
		private class DefineInfo {
			public string ClassName;
			public ConstructorInfo ctor;
			public Dictionary<string, string> Propertys = new Dictionary<string, string>();				
			
			public CustomGameObject Create() {
				CustomGameObject obj = (CustomGameObject)ctor.Invoke(new object[0]);
				foreach (string propName in Propertys.Keys) {
					string methodName = String.Format("Set{0}", propName.ToLower());
					MethodInfo method = obj.GetType().GetMethod(methodName,new Type[]{typeof(String)});
					if (method != null) {
						method.Invoke(obj, new object[]{ Propertys[propName]} );	
					}
				}
				obj.Init();
				return obj;
			}
		}
		
		Dictionary<char, DefineInfo> Defines = new Dictionary<char, DefineInfo>();
		
		void LoadLevel(XmlNode lvl) {
			#region загружаем дефайны
			Assembly asm = Assembly.GetExecutingAssembly();
			foreach (XmlNode def in lvl.SelectNodes("define")) {
				char defName = def.Attributes.GetNamedItem("name").Value[0];
				string defValue = def.Attributes.GetNamedItem("value").Value;							
				
				Type Tp = asm.GetType(defValue);
				if (Tp == null) {
					Console.WriteLine("Type {0} not fount", defValue);
				} else {
					DefineInfo di = new DefineInfo();
					di.ClassName = defValue;
					di.ctor = Tp.GetConstructor(new Type[0]);
					///чтение свойств
					foreach (XmlNode prop in def.SelectNodes("property")) {
						string propName = prop.Attributes.GetNamedItem("value").Value;
						string propValue = prop.InnerText;
						di.Propertys[propName] = propValue;
					}
					
					Defines[defName] = di;
				}
				
			}
			#endregion
			#region загружаем уровень
			List<string> lst = new List<string>();			
			string txt = lvl.SelectSingleNode("data").InnerText;
			foreach (string ln in txt.Split('\n')) {
				string tln = ln;
				tln = tln.Replace("\t", " ");
				tln = tln.Replace("\r", "");
				tln = tln.Replace("\n", "");
				int num = 0;
				if (int.TryParse(tln, out num)) {
					for(int i=0; i<num; i++);
					lst.Add("");
				} else {
					lst.Add(tln);
				}
			}			
			LevelData = lst.ToArray();
			#endregion
		}
		
		private bool CollisionTest(Polygon2D[] a, Polygon2D[] b) {			
			foreach (Polygon2D p1 in a) 
				foreach (Polygon2D p2 in b)
					if (Polygon2D.Intersect(p1, p2)) return true;
			return false;
		}

		ScreenSate State = ScreenSate.game;
		string ScreenMessage;
		float ScreenMessageTime = 0f;
		public override void Action (double time) {
			if (LevelLine > LevelData.Length && isBossDie) {
				//переход к следующему экрану
				ScreenMessageTime += 0.8f * (float)time;
				if (ScreenMessageTime > 1f)
					DoSwithToScreen(new NewMissionScreenClass(NextLevel));
			} else {
				if (Player.HP <= 0 && State == ScreenSate.game) {
					ScreenMessageTime = 0f;
					ScreenMessage = "Вы проиграли";
					State = ScreenSate.restart;
				}
				ActGameLoop(time);
				if (State == ScreenSate.restart) {
					ScreenMessageTime += 0.8f * (float)time;
					if (ScreenMessageTime > 1f) {
						Objects.Clear();
						Player = new PlayerClass();
						Objects.Add(Player);
						LevelLine = -1;
						FlyinLength = -1;
						Planet = null;
						State = ScreenSate.game;
					}
				}
			}								
			if (Planet != null) {				
				if (PlanetX > GameApp.ScreenWidth - Planet.Size / 3) PlanetX -= 70f * (float)time;
			}
		}
		
		void ActGameLoop(double time) {			
			FlyinLength+=  0.8f * (float)time;
			if ((int)FlyinLength > LevelLine) {
				LevelLine = (int)FlyinLength;
				if (LevelLine < LevelData.Length) {
					ExecuteLevelLine(LevelData[LevelLine]);	
				}
			}
			
			if (Shoot) Player.Shoot();
			if (Shoot2) Player.Shoot2();
			
			lock (Objects) {
				//Расчитываем все коллизии
				if (Objects.Count > 1) {
					for (int o1=0; o1<Objects.Count-1; o1++) {
						CustomGameObject obj1 = Objects[o1];
						if (obj1.CalcCollision) {
							Polygon2D[] obj1Shape = obj1.Shape;
							for (int o2=o1+1; o2<Objects.Count; o2++) {	
								CustomGameObject obj2 = Objects[o2];
								if (obj2.CalcCollision) {
									//todo: проверка на вхождение квадратов
									if (CollisionTest(obj1Shape, obj2.Shape)) {							
										obj1.OnCollision(obj2);	
										obj2.OnCollision(obj1);
									}
								}
							}
						}
					}
				}
				//Рассчитываем все остальное и удаляем ненужныео объекты
				for (int i=Objects.Count-1; i>=0; i--) {
					CustomGameObject o = (CustomGameObject) Objects[i];
					if (!o.Action(time)) {						
						Objects.RemoveAt(i);
						o.Die();
					}
				}
			}
		}
		
		void ExecuteLevelLine(string line) {
			string[] data = line.Split(':');
			if (data.Length>0) {
				BuildLevelLine(data[0]);
			}
			if (data.Length>1) {
				foreach (string sub in data[1].Split(';')) {
					ExecuteSub(sub);	
				}
			}
		}
		
		void BuildLevelLine(string line) {
			if (line.Length==0) return;
			float Sector = GameApp.ScreenHeight / (line.Length+1);
			int i = 1;
			foreach (char c in line) {				
				DefineInfo di = null;
				if (Defines.TryGetValue(c, out di)) {
					CustomGameObject obj = di.Create();					
					obj.Y = Sector * i;
					AddObject(obj);
				}
				i++;
			}
		}
		
		void ExecuteSub(string sub) {
			string [] args = sub.Split(new char[]{' '}, 2);
			if (args.Length==0) return;
			switch (args[0].ToLower()) {
				case  "msg":
					//Console.WriteLine(args[1]);
					break;
				case "planet":
					string[] argss = args[1].Split(' ');
					InsertPlanet(
					             argss[0],
					             int.Parse(argss[1]),
					             int.Parse(argss[2]));
					break;
			}	
		}
		
		public override void Draw () {
			GL.Clear(ClearBufferMask.ColorBufferBit | 
			         ClearBufferMask.DepthBufferBit);
			GameApp.Stars.Render();
			if (Planet != null) {
				Planet.Render(PlanetX, PlanetY);	
			}
			
			foreach (CustomGameObject o in Objects) {
				o.Draw();
			}	
			
			GL.Color3(new float[]{1f, 1f, 1f});
			for (int i=0; i<Player.HP; i++) {
				Heard.Render(i * Heard.Width, GameApp.ScreenHeight - Heard.Height);	
			}
			for (int i=0; i<Player.Energy; i++) {
				Energy.Render(GameApp.ScreenWidth - Energy.Width - i * Heard.Width, GameApp.ScreenHeight - Heard.Height);	
			}
						
			if (State != ScreenSate.game) {				
				GL.Color4(new float[] {1f, 1f, 1f, 1f-(float)ScreenMessageTime});
				GameApp.FontRenderBig.Render(ScreenMessage, 0, 0, 0);	
			}
		}
		
		public override void MouseMove (object sender, OpenTK.Input.MouseMoveEventArgs e) {						
			Player.TargetX = e.X;
			Player.TargetY = e.Y;
		}
		
		
		bool Shoot = false;
		bool Shoot2 = false;
		public override void MouseDown (object sender, MouseButtonEventArgs e) {
			switch(e.Button) {
			case MouseButton.Left:
				Shoot = true;
				break;
			case MouseButton.Right:
				Shoot2 = true;
				break;
			}
		}
		
		public override void MouseUp (object sender, MouseButtonEventArgs e) {			
			switch(e.Button) {
			case MouseButton.Left:
				Shoot = false;
				break;
			case MouseButton.Right:
				Shoot2 = false;
				break;
			}
		}

		
		private List<CustomGameObject> Objects = new List<CustomGameObject>();
		public void AddObject(CustomGameObject o) {
			lock(Objects) {
				Objects.Add(o);	
			}
		}
		
		bool isBossDie = false;
		public void BossDie() {
			State = ScreenSate.next;
			ScreenMessageTime = 0f;
			ScreenMessage = "Вы Выйграли!";
			isBossDie = true;	
		}
		
		PlanetRenderClass Planet = null;
		float PlanetX = 0;
		float PlanetY = 0;
		public void InsertPlanet(string texName,
		                         int position,
		                         int size) {
			TexClass tex = TexMan.LoadTexture(GameApp.GameFileName("data", "background", texName));
			Planet = new PlanetRenderClass(tex, size);
			PlanetX = GameApp.ScreenWidth + size;
			PlanetY = GameApp.ScreenHeight;
		}
	}
}
