/**********************************************************
RunUO AoV C# script file
Official Age of Valor Script :: www.uovalor.com
Last modified by Red Squirrel on Jan-01-2012 10:09:16pm
Filepath: scripts\Misc\AutoRestart.cs
Lines of code: 199

Description: 

***********************************************************/


using System;
using System.Threading;
using System.IO;
using System.Diagnostics;
using Server;
using System.Text;
using Server.Commands;
using Server.Network;
using System.Collections;
using System.Collections.Generic;

namespace Server.Misc
{
	public class AutoRestart
	{
		protected static bool m_Restarting=false;
		protected static DateTime m_restartdate;
		protected static DateTime m_lastwarning;
		protected static string m_RestartMsg;
		
		
		public static void Initialize()
		{
			CommandSystem.Register( "HardRestart", AccessLevel.Administrator, new CommandEventHandler( HardRestart_OnCommand ) );
			CommandSystem.Register( "Restart", AccessLevel.Administrator, new CommandEventHandler( Restart_OnCommand ),true );
			CommandSystem.Register( "cancelrestart", AccessLevel.Seer, new CommandEventHandler( CancelRestart_OnCommand ) );
			
			File.Delete("output\\restart.log");
			new RestartFileTimer().Start();
		}
		
		
		
		//this function does a few checks around the world to ensure it's safe to restart.  Only applies when we want a clean restart
		public static bool CanRestart()
		{	
			bool ret=true;
			string msg="N/A";
			
			//if some donations are in progress, don't want to restart till they're processed and the server has saved...
			if(Server.DonationSystem.DonationHandler.PendingDonations)
			{
				ret=false; 
				msg="donation data not in sync, allow a few minutes";
			}
			
		
		
			if(ret==false)
			{
				Console.WriteLine("Server restarted stopped: {0}",msg);
			}
		
			return true;
		}
		
		
		protected static void LogEntry(string text)
		{
			using ( StreamWriter sw = new StreamWriter( "output\\restart.log", true ) )
			{
				sw.WriteLine( "{0}: {1}", DateTime.Now, text );		
			}
		}
		
		
		public static void RestartNow()
		{			
			//don't restart if SQL connection is lost:
			if(DataStorage.SqlStorageSystem.LostCon)
				return;						
				
			
			
			LogEntry("Restart invoked");
			Process starter = new Process();

            starter.StartInfo.FileName   = "startserver.exe";
            starter.StartInfo.Arguments = Core.Arguments;
            
			try
			{		
				starter.Start();
			}
			catch
			{
				Console.WriteLine("Unable to find startserver.exe, will try starting core directly instead...");
				LogEntry("Failed to find startserver, restarting core directly...");
				Thread.Sleep(5000);
				Process.Start( Core.ExePath,Core.Arguments );
			}			
			
			LogEntry("Server process killed");
			Core.Process.Kill();
		}
		
		public static void CleanRestart()
		{						
			LogEntry("Clean restart invoked");			
			
			new CleanRestartCheckTimer().Start();	
		}		

		public static void HardRestart_OnCommand( CommandEventArgs e )
		{			
			RestartNow();			
		}		
		
		public static void Restart_OnCommand( CommandEventArgs e )
		{			
			CleanRestart();
		}
		
		public static void CancelRestart_OnCommand( CommandEventArgs e )
		{			
			if(m_Restarting)
			{
				m_Restarting=false;
				m_RestartMsg="";
				World.Broadcast(0x2b,false,"Notice: Server restart cancelled");
			}
		}

		private static TimeSpan GetMsgFreq()
		{
			int minutes=GetMinutesTillRestart();
			
			if(minutes<1)return TimeSpan.FromSeconds(30);
			if(minutes<5)return TimeSpan.FromMinutes(1);
			if(minutes<15)return TimeSpan.FromMinutes(5);
			if(minutes<30)return TimeSpan.FromMinutes(10);
			if(minutes<120)return TimeSpan.FromMinutes(30);
			
			return TimeSpan.FromHours(1);
		}
		
		
		private static int GetMinutesTillRestart()
		{
			if(!m_Restarting)return 1;
			
			TimeSpan ts = m_restartdate.Subtract(DateTime.Now);
			
			int seconds = (int)ts.TotalSeconds;
			
			if(seconds<=0)return 0;			
			else if(seconds<60)return 1;	
			return (int)Math.Floor((double)(seconds/60));
		}
		
		
		
		
		
		//checks if server needs to be restarted, and/or issues warning
		public static void Check()
		{
			if(!m_Restarting)return;
			
			int mintillrestart = GetMinutesTillRestart();
			
			//if time has elapsed OR there is no one online, we issue a restart
			if(mintillrestart==0 || Network.NetState.Instances.Count==0)
			{
				CleanRestart();
			
				return;
			}
			
			
			if((m_lastwarning + GetMsgFreq())<DateTime.Now)
			{
				IssueWarning(null);
				m_lastwarning=DateTime.Now;
			}
		}
		
		
		
		
		
		
		//issues restart warning.  Global if to is null
		public static void IssueWarning(Mobile to)
		{
			if(!m_Restarting)return;
			
			int mintillrestart = GetMinutesTillRestart();
			
			string line1=String.Format("Notice: The server is restarting in less than {0} minute{2} {1}",mintillrestart,m_RestartMsg,mintillrestart<=1 ? "" : "s");
			
			Console.WriteLine(line1);
			
			if(to==null)
			{
				World.Broadcast(0x2b,false,line1);
			}
			else
			{				
				to.SendMessage(0x2b,line1);				
			}
		}
		
		
		
		
		
		
		public class RestartFileTimer : Timer
		{
			
			
			public RestartFileTimer() : base( TimeSpan.FromSeconds(10),TimeSpan.FromSeconds(10))
			{
			}

			protected override void OnTick()
			{
				if(AutoRestart.m_Restarting)
				{
					AutoRestart.Check();
				
					return;
				}
				
				
				//check savequeue, ignore restart requests if it's too big:
				
				if(DataStorage.SqlStorageSystem.ActionObjectCount>10000)
					return;
					
				if(DataStorage.SqlStorageSystem.LostCon)
					return;
				
				
				//check if restart file exists:
				if(!File.Exists("restart.txt"))return;	
				
				// Read the file.
				// first line is time before restart, in minutes
				// Next line is displayed to players at certain intervals
				
				System.IO.StreamReader file =  new System.IO.StreamReader("restart.txt");
				
				string line1 = file.ReadLine();
				AutoRestart.m_RestartMsg = file.ReadLine();
				
				
				if(line1==null)line1="30";
				if(AutoRestart.m_RestartMsg!=null && AutoRestart.m_RestartMsg!="")AutoRestart.m_RestartMsg = "for " + AutoRestart.m_RestartMsg;

				file.Close();
				
				int interval = Convert.ToInt32(line1);
				
				if(interval<0)interval=0;			
				
				AutoRestart.m_Restarting=true;
				
				m_restartdate = DateTime.Now + TimeSpan.FromMinutes(interval);

				File.Delete("restart.txt");
				
				LogEntry("Restart file detected, server will restart automaticly");
				
				AutoRestart.Check();
			}
		}
		
		
		public class CleanRestartCheckTimer : Timer
		{
			public CleanRestartCheckTimer() : base( TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(1))
			{
			}
			
			protected override void OnTick()
			{				

				if(AutoRestart.CanRestart())
				{
					//restart warning...
					World.Broadcast( 0x22, true, "The server is going down now..." );			
					Console.WriteLine("AutoRestart.CleanRestart() called... server going down now!");					
					
					
					
					//Gather all players to disconnect them.  The reason to do it this way is so any logout info is set properly and saved:
				
					List<NetState> list = NetState.Instances;

					for ( int i = 0; i < list.Count; ++i )
					{
						if ( list[i].Mobile != null )
						{					
							list[i].Dispose();
						}
					}
					
					
					//do full save then restart:
					
					DataStorage.SqlStorageSystem.DoFullSave(true);
					AutoRestart.RestartNow();		
				}
			}
		}
		
		
		
	}
}