using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Diagnostics;

using Server;
using Server.Commands;
using Server.Network;
using Server.Items;
using Server.Mobiles;
using Server.Misc;
using Server.Accounting;
using Server.Gumps;
using Server.Guilds;

namespace Server.Misc
{
	public class AutoSave : Timer
	{
		private static TimeSpan m_Delay = TimeSpan.FromMinutes(20.0);
		//private static TimeSpan m_Warning = TimeSpan.Zero;
		private static TimeSpan m_Warning = TimeSpan.FromSeconds(15.0);

		public static void Initialize()
		{
			new AutoSave().Start();
			CommandSystem.Register("SetSaves", AccessLevel.GameMaster, new CommandEventHandler(SetSaves_OnCommand));
		}

		private static bool m_SavesEnabled = true;

		public static bool SavesEnabled
		{
			get { return m_SavesEnabled; }
			set { m_SavesEnabled = value; }
		}

		[Usage("SetSaves <true | false>")]
		[Description("Enables or disables automatic shard saving.")]
		public static void SetSaves_OnCommand(CommandEventArgs e)
		{
			if (e.Length == 1)
			{
				m_SavesEnabled = e.GetBoolean(0);
				e.Mobile.SendMessage("Saves have been {0}.", m_SavesEnabled ? "enabled" : "disabled");
			}
			else
			{
				e.Mobile.SendMessage("Format: SetSaves <true | false>");
			}
		}

		public AutoSave() : base(m_Delay - m_Warning, m_Delay)
		{
			Priority = TimerPriority.OneMinute;
		}

		protected override void OnTick()
		{
			if (!m_SavesEnabled || AutoRestart.Restarting)
				return;

			if (m_Warning == TimeSpan.Zero)
			{
				Save();
			}
			else
			{
				int s = (int)m_Warning.TotalSeconds;
				int m = s / 60;
				s %= 60;

				if (m > 0 && s > 0)
					World.Broadcast(0x35, true, "The world will save in {0} minute{1} and {2} second{3}.", m, m != 1 ? "s" : "", s, s != 1 ? "s" : "");
				else if (m > 0)
					World.Broadcast(0x35, true, "The world will save in {0} minute{1}.", m, m != 1 ? "s" : "");
				else
					World.Broadcast(0x35, true, "Ulozeni sveta probehne za {0} sekund.", s);

				Timer.DelayCall(m_Warning, new TimerCallback(Save));
			}
		}

		public static void Save()
		{
			ArrayList mobs = new ArrayList(World.Mobiles.Values);

			if (AutoRestart.Restarting)
				return;

			try { Backup(); }
			catch { }

			foreach (Mobile m in mobs)
			{
				m.SendGump (new SaveGump());
			}

			World.Save(false);
			foreach (Mobile m in mobs)
			{
				m.CloseGump(typeof(SaveGump));
			}

			ZubyToFile(mobs);
			GuildsToFile();
			Uspechy.CheckNewRound();
		}

		private static void ZubyToFile(ArrayList mobs)
		{
			ArrayList ZubyArray = new ArrayList();
			ulong gold_tith = 0; ulong gold_check = 0; ulong gold_vendors = 0; ulong gold = 0; ulong silver = 0; ulong cech = 0;

			foreach (Item item in World.Items.Values)
			{
				if (item is Gold)
					gold += (ulong) item.Amount;
				else if (item is BankCheck)
					gold_check += (ulong) ((BankCheck)item).Worth;
				else if (item is CheckBook)
					gold_check += (ulong) ((CheckBook)item).Cash;
				else if (item is TokenCheck)
					silver += (ulong) ((TokenCheck)item).Worth;
				else if (item is TokenBox)
					silver += (ulong) ((TokenBox)item).Token;
				else if (item is CechovniMince)
					cech += (ulong) item.Amount;
			}

			foreach (Mobile m in mobs)
			{
				if (m is PlayerMobile)
				{
					gold_tith += (ulong) m.TithingPoints;

					PlayerMobile pm = (PlayerMobile) m;

					if (pm.Zuby > 0)
					{
						ZubyArray.Add(pm);
					}
				}
				else if (m is PlayerVendor)
					gold_vendors += (ulong) ((PlayerVendor)m).HoldGold;
			}

			if (!Directory.Exists("web"))
				Directory.CreateDirectory("web");

			using (StreamWriter op = new StreamWriter("web/zuby.html"))
			{
				op.WriteLine("<h4>Měna:</h4><br />");
				op.WriteLine("<b>Zlato:</b><br />&nbsp;-&nbsp;Celkem:&nbsp;" + (gold + gold_check + gold_tith + gold_vendors).ToString("N0", Auberon.Chat.General.Nfi));
				op.WriteLine("<br />&nbsp;-&nbsp;Hotovost:&nbsp;" + gold.ToString("N0", Auberon.Chat.General.Nfi));
				op.WriteLine("<br />&nbsp;-&nbsp;Šeky:&nbsp;" + gold_check.ToString("N0", Auberon.Chat.General.Nfi));
				op.WriteLine("<br />&nbsp;-&nbsp;U hráčských vendorů:&nbsp;" + gold_vendors.ToString("N0", Auberon.Chat.General.Nfi));
				op.WriteLine("<br />&nbsp;-&nbsp;Tithing points:&nbsp;" + gold_tith.ToString("N0", Auberon.Chat.General.Nfi));

				op.WriteLine("<br /><br /><b>Stříbro:</b>&nbsp;" + silver.ToString("N0", Auberon.Chat.General.Nfi));
				op.WriteLine("<br /><b>Cechovni mince:</b>&nbsp;" + cech.ToString("N0", Auberon.Chat.General.Nfi));

				op.WriteLine("<br /><br /><h4>Auberonovy zuby [Top 25]:</h4><br />");
				op.WriteLine("&nbsp;-&nbsp;Konec kola:&nbsp;" + String.Format("{0:D}<br /><br />", SoutezeLog.ZubyEndRound));
			}

			if (ZubyArray != null && ZubyArray.Count > 0)
			{
				ZubyArray.Sort(new SoutezeLog.ZubyListAmountSorter());
				int count = ZubyArray.Count > 24 ? 25 : ZubyArray.Count;

				using (StreamWriter op = new StreamWriter("web/zuby.html", true))
				{
					op.WriteLine("<table>");

					for (int i = 0; i < count; i++)
					{
						if (ZubyArray[i] == null | !(ZubyArray[i] is PlayerMobile)) { continue; }
						else
						{
							PlayerMobile pm = (PlayerMobile) ZubyArray[i];
							op.WriteLine("<tr><td>" + StatusPage.Encode(pm.RawName) + "</td><td>" + pm.Zuby + "</td></tr>");
						}
					}

					op.WriteLine("</table>");
				}
			}
			else
			{
				using (StreamWriter op = new StreamWriter("web/zuby.html", true))
				op.Write("Zatim nikdo nenasel zadny auberonuv zub");
			}

			ZubyArray.Clear();
		}

		private static void GuildsToFile()
		{
			List<Guild> m_List = new List<Guild>();

			foreach(KeyValuePair<int, BaseGuild> kvp in Guild.List)
			{
				if(kvp.Value is Guild)
					m_List.Add((Guild)kvp.Value);
			}

			m_List.Sort(GuildComparer.Instance);

			if (!Directory.Exists("web"))
				Directory.CreateDirectory("web");

			if (m_List != null && m_List.Count > 0)
			{
				int count = m_List.Count;

				using (StreamWriter op = new StreamWriter("web/guilds.html"))
				{
					op.WriteLine("<p>Počet guild: " + count.ToString() + "<br />Registrační poplatek: " + Guild.RegistrationFee.ToString("#,0") + "gp</p>");
					op.WriteLine("<table><tr><th>Lvl</th><th>Název</th><th>Seznam členů</th></tr>");
					for (int i = 0; i < count; i++)
					{
						Guild g = m_List[i];

						if(g == null || g.Name == null || g.Name == "" || g.Members.Count < 2)
							continue;
						else
						{
							op.WriteLine("<tr><td><strong>" + g.Level.ToString() + "</strong></td><td><strong>" + StatusPage.Encode(g.Name) + " [" + StatusPage.Encode(g.Abbreviation) + "]</strong>");

							if (g.Website != null && g.Website != "")
								op.Write("<br />Web: " + StatusPage.Encode(g.Website));

							if (g.Charter != null && g.Charter != "")
								op.Write("<br />Popis: " + StatusPage.Encode(g.Charter));

							op.WriteLine("</td><td>");

							for (int c = 0; c < g.Members.Count; c++)
							{
								Mobile m = g.Members[c];

								if(m == null)
									continue;

								string title = m.GuildTitle;

								if (title != null)
									title = " [" + StatusPage.Encode(title.Trim()) + "]";
								else
									title = "";

								op.Write(StatusPage.Encode(m.RawName) + title);

								if (g.Leader == m)
									op.Write("<span title=\"Guild Master\">[GM]</span>");

								if (c < (g.Members.Count - 1))
									op.Write("<br />");
							}

							op.WriteLine("</td></tr>");
						}
					}

					op.WriteLine("</table>");
				}
			}
			else
			{
				using (StreamWriter op = new StreamWriter("web/guilds.html"))
				op.WriteLine("Nejsou zadne guildy");
			}

			m_List.Clear();
		}

		public class GuildComparer : IComparer<Guild>
		{
			public static readonly IComparer<Guild> Instance = new GuildComparer();

			public int Compare(Guild x, Guild y)
			{
				if(x == null && y == null)
					return 0;
				else if(x == null)
					return -1;
				else if(y == null)
					return 1;

				return Insensitive.Compare(x.Name, y.Name);
			}
		}

		private static string[] m_Backups = new string[]
		{

			"20-zaloha",
			"19-zaloha",
            "18-zaloha",
			"17-zaloha",
			"16-zaloha",
			"15-zaloha",
			"14-zaloha",
			"13-zaloha",
			"12-zaloha",
            "11-zaloha",
			"10-zaloha",
            "09-Devata zaloha",
			"08-Osma zaloha",
			"07-Sedma zaloha",
			"06-Sesta zaloha",
			"05-Pata zaloha",
			"04-Ctvrta zaloha",
			"03-Treti zaloha",
            "02-Druha zaloha",
			"01-Prvni zaloha",
			"00-Nejnovejsi zaloha"
		};

		private static void Backup()
		{
			if (m_Backups.Length == 0)
				return;

			string root = Path.Combine(Core.BaseDirectory, "Backups\\Automatic");

			if (!Directory.Exists(root))
				Directory.CreateDirectory(root);

			string[] existing = Directory.GetDirectories(root);

			for (int i = 0; i < m_Backups.Length; ++i)
			{
				DirectoryInfo dir = Match(existing, m_Backups[i]);

				if (dir == null)
					continue;

				if (i > 0)
				{
					string timeStamp = FindTimeStamp(dir.Name);

					if (timeStamp != null)
					{
						try { dir.MoveTo(FormatDirectory(root, m_Backups[i - 1], timeStamp)); }
						catch { }
					}
				}
				else
				{
					try { dir.Delete(true); }
					catch { }
				}
			}

			string saves = Path.Combine(Core.BaseDirectory, "Saves");

			if (Directory.Exists(saves))
				Directory.Move(saves, FormatDirectory(root, m_Backups[m_Backups.Length - 1], GetTimeStamp()));
		}

		private static DirectoryInfo Match(string[] paths, string match)
		{
			for (int i = 0; i < paths.Length; ++i)
			{
				DirectoryInfo info = new DirectoryInfo(paths[i]);

				if (info.Name.StartsWith(match))
					return info;
			}

			return null;
		}

		private static string FormatDirectory(string root, string name, string timeStamp)
		{
			return Path.Combine(root, String.Format("{0} ({1})", name, timeStamp));
		}

		private static string FindTimeStamp(string input)
		{
			int start = input.IndexOf('(');

			if (start >= 0)
			{
				int end = input.IndexOf(')', ++start);

				if (end >= start)
					return input.Substring(start, end-start);
			}

			return null;
		}

		private static string GetTimeStamp()
		{
			DateTime now = DateTime.Now;

			return String.Format("{0}-{1}-{2} {3}-{4:D2}-{5:D2}",
					now.Day,
					now.Month,
					now.Year,
					now.Hour,
					now.Minute,
					now.Second
				);
		}
	}
}