using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Text;
using System.IO;

namespace pcd_g1
{
	class StgAiStage : ScrollableStage
	{
		public HPWidghet HPWidghet { get; private set; }

		private Dictionary<IStageObject, string> flaggedStageObjects = new Dictionary<IStageObject, string>();

		string backPict = "";
		public StgAiStage(string aiFileName)
			: base()
		{
			PropertyVal val = loadAiFile(aiFileName);

			if (val.EnableMainCharacter)
			{
				MainCharacter = new MainCharacterStageObject(
					val.MainCharacterLocationX,
					val.MainCharacterLocationY, this);
				Add(MainCharacter);
			}

			if (aiFileName.Substring(aiFileName.LastIndexOf('\\') + 1).Contains("mizuumi")) IsLake = true;

			ScrollRangeX = val.Width;
			ScrollRangeY = val.Height;

			if (val.LeftStg != "")
			{
				for (int i = 0; i < val.Height; i += 32)
				{
					Add(new TouchStageJumpItemStageObject(-32, i, this, val.LeftStg, val.LeftStagePointX, val.LeftStagePointY));
				}
			}
			if (val.RightStg != "")
			{
				for (int i = 0; i < val.Height; i += 32)
				{
					Add(new TouchStageJumpItemStageObject(val.Width, i, this, val.RightStg, val.RightStagePointX, val.RightStagePointY));
				}
			}

			backPict = val.Background;

			if (val.BackgroundMusic != "*")
			{
				MainForm.PlaySound(val.BackgroundMusic);
			}

			//HPゲージ
			HPWidghet = new HPWidghet((MainCharacterStageObject)MainCharacter);
			if(!val.NoWidghet) MainForm.form.Widghets.Add(HPWidghet);
		}

		class PropertyVal
		{
			public bool EnableMainCharacter = true;
			public int MainCharacterLocationX = 64;
			public int MainCharacterLocationY = 64;
			public string Background = "";
			public string BackgroundMusic = "*";
			public int Width = 640;
			public int Height = 480;
			public string LeftStg = "";
			public int LeftStagePointX = -1;
			public int LeftStagePointY = -1;
			public string RightStg = "";
			public int RightStagePointX = -1;
			public int RightStagePointY = -1;
			public bool NoWidghet = false;
		}

		private PropertyVal loadAiFile(string aiFileName)
		{
			Debug.WriteLine("#STGAI: Loading... :" + aiFileName);
			PropertyVal ret = new PropertyVal();
			int currentY = 0;
			int defaultCurrentX = 0;
			var reader = new StreamReader(aiFileName);
			var idDict = new Dictionary<char, int>();
			var paramDict = new Dictionary<char, string>();
			var flagDict = new Dictionary<char, string>();
			var commandDict = new Dictionary<char, Storyboard>();
			char commandChar = '\0';
			Storyboard commandObject = null;
			Storyboard commandObjectOkStory = null;
			Storyboard commandObjectNgStory = null;
			string commandFlag = "";
			string commandParam = "";
			while (!reader.EndOfStream)
			{
				string line = reader.ReadLine();
				if (line != "" && !line.StartsWith("'"))
				{
					if (line[0] == '|')
					{
						if (commandObject != null)
						{
							string cmdName = line.Substring(1), cmdParam = "";
							if (line.Contains(':'))
							{
								cmdName = line.Substring(1, line.IndexOf(':') - 1);
								cmdParam = line.Substring(line.IndexOf(':') + 1);
							}
							Storyboard addingStoryboard = commandObject;
							if (cmdName.StartsWith("OK"))
							{
								cmdName = cmdName.Substring(2);
								if (commandObjectOkStory == null)
								{
									writeErr("選択コマンドが無いので接頭語OKは無効です : " + cmdName);
								}
								else
								{
									addingStoryboard = commandObjectOkStory;
								}
							}
							if (cmdName.StartsWith("NG"))
							{
								cmdName = cmdName.Substring(2);
								if (commandObjectNgStory == null)
								{
									writeErr("選択コマンドが無いので接頭語NGは無効です : " + cmdName);
								}
								else
								{
									addingStoryboard = commandObjectNgStory;
								}
							}
							switch (cmdName)
							{
								case "Message":		//param=メッセージの内容
									addingStoryboard.Add(new MessageStoryBoard(cmdParam.Replace("\\n", "\n")));
									break;
								case "SelectDialog": //param=メッセージ,結果を入れるフラグ,選択肢1,選択肢2,...
									string[] args = cmdParam.Split(',');
									if (args.Length > 2)
									{
										var tmp = new List<string>();
										for (int i = 2; i < args.Length; i++) tmp.Add(args[i]);
										var oks = new Storyboard();
										var ngs = new Storyboard();
										var brd = new SelectStoryBoard(args[0], 400, 150, 230, 140, oks, ngs, tmp.ToArray());
										brd.Temp = args[1];
										brd.OnStoryEnded += new StoryEndedEventHandler((IStoryboard b) => StageManager.CurrentDataSet.Set(((SelectStoryBoard)b).Temp, ((SelectStoryBoard)b).Result));
										addingStoryboard.Add(brd);
										commandObjectOkStory = oks;
										commandObjectNgStory = ngs;
									}
									else
									{
										writeErr("SelectDialogの引数が足りません");
									}
									break;
								case "FlagIf": //param=フラグ名
									var okstg = new Storyboard();
									var ngstg = new Storyboard();
									string fg = cmdParam;
									bool not = false;
									if (fg.StartsWith("!"))
									{
										not = true;
										fg = fg.Substring(1);
									}
									addingStoryboard.Add(new ExecuteStoryBoard<string>((s) => { if (StageManager.CurrentDataSet.GetInt(s) > 0) { okstg.Run(); } else { ngstg.Run(); } }, fg));
									if (not)
									{
										commandObjectOkStory = ngstg;
										commandObjectNgStory = okstg;
									}
									else
									{
										commandObjectOkStory = okstg;
										commandObjectNgStory = ngstg;
									}
									break;
								case "CharacterMessage":
									if (cmdParam.Contains(','))
									{
										string img = cmdParam.Remove(cmdParam.IndexOf(','));
										string msg = cmdParam.Substring(cmdParam.IndexOf(',') + 1);
										addingStoryboard.Add(new CharacterMessageStoryBoard(msg.Replace("\\n", "\n"), ImageManager.Search(img)));
									}
									else
									{
										writeErr("CharacterMessageの引数が間違っています");
									}
									break;
								case "Flag":
									if (cmdParam.Contains(','))
									{
										string key = cmdParam.Remove(cmdParam.IndexOf(','));
										string value = cmdParam.Substring(cmdParam.IndexOf(',') + 1);
										int val;
										if (int.TryParse(value, out val))
										{
											var item = new ExecuteStoryBoard<string>((s) => StageManager.CurrentDataSet.Set(s.Remove(s.IndexOf(',')), int.Parse(s.Substring(s.IndexOf(',') + 1))), cmdParam);
											addingStoryboard.Add(item);
										}
										else
										{
											writeErr("Flagの値を数値に変換できません");
										}
									}
									else
									{
										writeErr("Flagの引数が間違っています");
									}
									break;
								case "Stage"://StageName[,X,Y]
									int x = -1, y = -1;
									string[] arglst = cmdParam.Split(',');
									if (arglst.Length >= 3)
									{
										x = int.Parse(arglst[1]);
										y = int.Parse(arglst[2]);
									}
									addingStoryboard.Add(new ExecuteStoryBoard<string>((s) => StageManager.RunStage(s, x, y), arglst[0]));
									break;
							}
						}
						else
						{
							writeErr("ここに'|'を使用することはできません。行:" + line);
						}
					}
					else
					{
						if (commandObject != null)
						{
							idDict.Add(commandChar, 999);
							paramDict.Add(commandChar, commandParam);
							if (commandFlag != "") flagDict.Add(commandChar, commandFlag);
							commandDict.Add(commandChar, commandObject);
							commandObject = null;
						}
						if (line[0] == '#')	//属性の設定
						{
							if (line.Contains(' ')) line = line.Remove(line.IndexOf(' '));	//コメント除去
							if (line.Contains('='))
							{
								string n = line.Substring(1, line.IndexOf('=') - 1);
								string v = line.Substring(line.IndexOf('=') + 1);
								switch (n)
								{
									case "EnableMainCharacter":
										ret.EnableMainCharacter = v == "true";
										break;
									case "MainCharacterLocationX":
										if (!int.TryParse(v, out ret.MainCharacterLocationX))
										{
											writeErr("MainCharacterLocationXの値を整数として認識できません 値:" + v);
										}
										break;
									case "MainCharacterLocationY":
										if (!int.TryParse(v, out ret.MainCharacterLocationY))
										{
											writeErr("MainCharacterLocationYの値を整数として認識できません 値:" + v);
										}
										break;
									case "Background":
										ret.Background = v;
										break;
									case "BackgroundMusic":
										ret.BackgroundMusic = v;
										break;
									case "LeftStage":
										string[] args = v.Split(',');
										ret.LeftStg = args[0];
										if (args.Length == 3)
										{
											int x, y;
											if (int.TryParse(args[1], out x) && int.TryParse(args[2], out y))
											{
												ret.LeftStagePointX = x;
												ret.LeftStagePointY = y;
											}
											else
											{
												writeErr("LeftStageの座標を数値に変換できませんでした");
											}
										}
										break;
									case "RightStage":
										string[] args2 = v.Split(',');
										ret.RightStg = args2[0];
										if (args2.Length == 3)
										{
											int x, y;
											if (int.TryParse(args2[1], out x) && int.TryParse(args2[2], out y))
											{
												ret.RightStagePointX = x;
												ret.RightStagePointY = y;
											}
											else
											{
												writeErr("RightStageの座標を数値に変換できませんでした");
											}
										}
										break;
									case "CurrentX":				//配置を開始する位置
										if (!int.TryParse(v, out defaultCurrentX))
										{
											writeErr("CurrentXの値を整数として認識できません 値:" + v);
										}
										break;
									case "CurrentY":				//配置を開始する位置
										if (!int.TryParse(v, out currentY))
										{
											writeErr("CurrentYの値を整数として認識できません 値:" + v);
										}
										break;
									case "Width":					//ブロック数単位
										if (!int.TryParse(v, out ret.Width))
										{
											writeErr("Widthの値を整数として認識できません 値:" + v);
										}
										break;
									case "Height":					//ブロック数単位
										if (!int.TryParse(v, out ret.Height))
										{
											writeErr("Heightの値を整数として認識できません 値:" + v);
										}
										break;
									case "NoWidghet":
										ret.NoWidghet = v != "";
										break;
									default:
										writeErr("認識できない属性名 :" + n);
										break;
								}
							}
							else
							{
								writeErr("属性値の設定には'='を用います。ライン: " + line);
							}
						}
						else if (line[0] == ':')	//シンボルの設定
						{
							if (line.Contains(' ')) line = line.Remove(line.IndexOf(' '));	//コメント除去
							if (line.Contains('='))
							{
								string k = line.Substring(1, line.IndexOf('='));
								if (k.Length > 0)
								{
									string v = line.Substring(line.IndexOf('=') + 1);
									string param = "";
									string flag = "";
									if (v.Contains(':'))
									{
										param = v.Substring(v.IndexOf(':') + 1);
										v = v.Remove(v.IndexOf(':'));
										if (param.Contains(':'))
										{
											flag = param.Substring(param.IndexOf(':') + 1);
											param = param.Remove(param.IndexOf(':'));
										}
									}
									int vi;
									if (!int.TryParse(v, out vi))
									{
										writeErr("シンボルの値を整数値に変換できません。名前:" + k);
									}
									else
									{
										if (vi == 999)
										{
											commandChar = k[0];
											commandObject = new Storyboard();
											commandParam = param;
											if (flag != "") flagDict.Add(k[0], flag);
										}
										else
										{
											idDict.Add(k[0], vi);
											paramDict.Add(k[0], param);
											if (flag != "") flagDict.Add(k[0], flag);
										}
									}
								}
								else
								{
									writeErr("シンボルの名前が空文字であり無効です。行:" + line);
								}
							}
							else
							{
								writeErr("シンボルの設定には'='を用います。行:" + line);
							}
						}
						else						//シンボルの列挙
						{
							int currentX = defaultCurrentX;
							for (int i = 0; i < line.Length; i++, currentX++)
							{
								char ch = line[i];
								if (ch != ' ' && ch != '　')
								{
									if (idDict.ContainsKey(ch))
									{
										int id = idDict[ch];
										IStageObject item = null;
										try
										{
											item = ReadStgFile.GetStageObjectFromID(id, currentX * 32, currentY * 32, this,
																						paramDict.ContainsKey(ch) ? paramDict[ch] : "");
										}
										catch (Exception)
										{
											writeErr("アイテム" + ch + "中でエラーが発生しました");
											continue;
										}
										if (item is CommandableItemStageObject)
										{
											string[] args = paramDict[ch].Split(',');
											if (args.Length == 3)
											{
												CommandableItemStageObject.CommandTiming timing = CommandableItemStageObject.CommandTiming.Now;
												switch (args[0])
												{
													case "Touch": timing = CommandableItemStageObject.CommandTiming.Touch; break;
													case "Flag": timing = CommandableItemStageObject.CommandTiming.Flag; break;
													case "DownKey": timing = CommandableItemStageObject.CommandTiming.DownKey; break;
													case "Time": timing = CommandableItemStageObject.CommandTiming.Time; break;
													case "Now": timing = CommandableItemStageObject.CommandTiming.Now; break;
												}
												((CommandableItemStageObject)item).AddCommand(timing, commandDict[ch], args[1]);
											}
										}
										if (flagDict.ContainsKey(ch))
										{
											flaggedStageObjects.Add(item, flagDict[ch]);
										}
										else
										{
											Add(item);
										}

									}
									else
									{
										writeErr("定義されていないシンボル: " + ch);
									}
								}
							}
							currentY++;
						}
					}
				}
			}

			return ret;
		}

		private void writeErr(string error, bool stop = false)
		{
			if (stop)
			{
				Debug.WriteLine("STGAI Error : " + error);
				//throw new ApplicationException(error);
			}
			else
			{
				Debug.WriteLine("STGAI Warning : " + error);
			}
		}

		int animcnt = 0;
		public override void Draw(Graphics g, int width, int height)
		{
			//背景画像の描画
			var imgLoc = new Point(-(ScrollX / 2) % width, -(ScrollY / 2) % height);
			string imgname = backPict;
			string rainName = "";

			if (imgname.StartsWith("rain:"))
			{
				imgname = imgname.Substring(5);
				rainName = "rain-boss-" + (animcnt > 5 ? "1" : "2");
				if (++animcnt > 10) animcnt = 0;
			}

			if (imgname == "black")
			{
				g.FillRectangle(Brushes.Black, 0, 0, width, height);
			}
			else
			{
				if (imgname == "rain-boss")
				{
					imgname += "-" + (animcnt > 5 ? "1" : "2");
					if (++animcnt > 10) animcnt = 0;
					g.DrawImage(ImageManager.Search("back-air-future"), imgLoc.X, imgLoc.Y, width, height);
					g.DrawImage(ImageManager.Search("back-air-future"), imgLoc.X + width, imgLoc.Y, width, height);
				}
				var img = ImageManager.Search(imgname);
				imgLoc = new Point(-(ScrollX / 2) % img.Width, -(ScrollY / 2) % img.Height);
				g.DrawImage(img, imgLoc.X, imgLoc.Y, img.Width, img.Height);
				g.DrawImage(img, imgLoc.X + img.Width, imgLoc.Y, img.Width, img.Height);
				g.DrawImage(img, imgLoc.X, imgLoc.Y + img.Height, img.Width, img.Height);
				g.DrawImage(img, imgLoc.X + img.Width, imgLoc.Y + img.Height, img.Width, img.Height);
			}

			if (rainName != "")
			{
				g.DrawImage(ImageManager.Search(rainName), imgLoc.X, imgLoc.Y, width, height);
				g.DrawImage(ImageManager.Search(rainName), imgLoc.X + width, imgLoc.Y, width, height);
				g.DrawImage(ImageManager.Search(rainName), imgLoc.X, imgLoc.Y + height, width, height);
				g.DrawImage(ImageManager.Search(rainName), imgLoc.X + width, imgLoc.Y + height, width, height);
			}
			
			

			base.Draw(g, width, height);
		}

		public override void Update(int keyStatus)
		{
			base.Update(keyStatus);

			for (int i = 0; i < flaggedStageObjects.Count; i++)
			{
				IStageObject j = flaggedStageObjects.Keys.ToList()[i];
				string k = flaggedStageObjects[j];
                bool isTrue = true;
                foreach (string s in k.Split('&'))
                {
                    string flag = s;
                    bool not = flag.StartsWith("!");
                    if (not) flag = flag.Substring(1);
                    if ((StageManager.CurrentDataSet.GetInt(flag) > 0) == not)
                    {
                        isTrue = false;                   
                    }
                }
                if (isTrue)
                {
                    if (!Contains(j))
                        Add(j);
                }
                else
                {
                    if (Contains(j))
                        Remove(j);
                }
			}
		}
	}
}
