context: using System;
  using System.IO;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;
  using Server;
  using Server.Misc;
  using Server.Items;
  using Server.Guilds;
  using Server.Mobiles;
  using Server.Accounting;
  using Server.Commands;
  
  namespace Server.Engines.MyRunUO
  {
  	public class MyRunUO : Timer
  	{
  		private static double CpuInterval = 0.1; // Processor runs every 0.1 seconds
  		private static double CpuPercent = 0.25; // Processor runs for 25% of Interval, or ~25ms. This should take around 25% cpu
  
  		public static void Initialize()
  		{
  			if ( Config.Enabled )
  			{
  				Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), Config.CharacterUpdateInterval, new TimerCallback( Begin ) );
  
  				CommandSystem.Register( "InitMyRunUO", AccessLevel.Administrator, new CommandEventHandler( InitMyRunUO_OnCommand ) );
  				CommandSystem.Register( "UpdateMyRunUO", AccessLevel.Administrator, new CommandEventHandler( UpdateMyRunUO_OnCommand ) );
  
  				CommandSystem.Register( "PublicChar", AccessLevel.Player, new CommandEventHandler( PublicChar_OnCommand ) );
  				CommandSystem.Register( "PrivateChar", AccessLevel.Player, new CommandEventHandler( PrivateChar_OnCommand ) );
  			}
  		}
  
  		[Usage( "PublicChar" )]
  		[Description( "Enables showing extended character stats and skills in MyRunUO." )]
  		public static void PublicChar_OnCommand( CommandEventArgs e )
  		{
  			PlayerMobile pm = e.Mobile as PlayerMobile;
  
  			if ( pm != null )
  			{
  				if ( pm.PublicMyRunUO )
  				{
  					pm.SendMessage( "You have already chosen to show your skills and stats." );
  				}
  				else
  				{
  					pm.PublicMyRunUO = true;
  					pm.SendMessage( "All of your skills and stats will now be shown publicly in MyRunUO." );
  				}
  			}
  		}
  
  		[Usage( "PrivateChar" )]
  		[Description( "Disables showing extended character stats and skills in MyRunUO." )]
  		public static void PrivateChar_OnCommand( CommandEventArgs e )
  		{
  			PlayerMobile pm = e.Mobile as PlayerMobile;
  
  			if ( pm != null )
  			{
  				if ( !pm.PublicMyRunUO )
  				{
  					pm.SendMessage( "You have already chosen to not show your skills and stats." );
  				}
  				else
  				{
  					pm.PublicMyRunUO = false;
  					pm.SendMessage( "Only a general level of your top three skills will be shown in MyRunUO." );
  				}
  			}
  		}
  
  		[Usage( "InitMyRunUO" )]
  		[Description( "Initially creates the database schema." )]
  		public static void InitMyRunUO_OnCommand( CommandEventArgs e )
  		{
  			if ( m_Command != null && m_Command.HasCompleted )
  			{
  				TableCreation();
  				e.Mobile.SendMessage( "MyRunUO table creation process has been started." );
  			}
  			else
  			{
  				e.Mobile.SendMessage( "MyRunUO table creation is already running." );
  			}
  		}
  
  		public static void TableCreation()
  		{
  			if ( m_Command != null && !m_Command.HasCompleted )
  				return;
  			DateTime start = DateTime.Now;
  			Console.WriteLine( "MyRunUO: Creating tables" );
  			try
  			{
  				m_Command = new DatabaseCommandQueue( "MyRunUO: Tables created in {0:F1} seconds", "MyRunUO Status Database Thread" );
  				m_Command.Enqueue( "CREATE TABLE IF NOT EXISTS `myrunuo_characters` ( `char_id` int(10) unsigned NOT NULL, `char_name` varchar(255) NOT NULL, `char_str` smallint(3) unsigned NOT NULL, `char_dex` smallint(3) unsigned NOT NULL, `char_int` smallint(3) unsigned NOT NULL, `char_female` tinyint(1) NOT NULL, `char_counts` smallint(6) NOT NULL, `char_guild` varchar(255) DEFAULT NULL, `char_guildtitle` varchar(255) DEFAULT NULL, `char_nototitle` varchar(255) DEFAULT NULL, `char_bodyhue` smallint(5) unsigned DEFAULT NULL, `char_public` tinyint(1) NOT NULL, PRIMARY KEY (`char_id`))" );
  				m_Command.Enqueue( "CREATE TABLE IF NOT EXISTS `myrunuo_characters_layers` ( `char_id` int(10) unsigned NOT NULL, `layer_id` tinyint(3) unsigned NOT NULL, `item_id` smallint(5) unsigned NOT NULL, `item_hue` smallint(5) unsigned NOT NULL, PRIMARY KEY (`char_id`,`layer_id`))" );
  				m_Command.Enqueue( "CREATE TABLE IF NOT EXISTS `myrunuo_characters_skills` ( `char_id` int(10) unsigned NOT NULL, `skill_id` tinyint(3) NOT NULL, `skill_value` smallint(5) unsigned NOT NULL, PRIMARY KEY (`char_id`,`skill_id`),  KEY `skill_id` (`skill_id`))" );
  				m_Command.Enqueue( "CREATE TABLE IF NOT EXISTS `myrunuo_guilds` ( `guild_id` smallint(5) unsigned NOT NULL, `guild_name` varchar(255) NOT NULL, `guild_abbreviation` varchar(8) DEFAULT NULL, `guild_website` varchar(255) DEFAULT NULL, `guild_charter` varchar(255) DEFAULT NULL, `guild_type` varchar(8) NOT NULL, `guild_wars` smallint(5) unsigned NOT NULL, `guild_members` smallint(5) unsigned NOT NULL, `guild_master` int(10) unsigned NOT NULL, PRIMARY KEY (`guild_id`))" );
  				m_Command.Enqueue( "CREATE TABLE IF NOT EXISTS `myrunuo_guilds_wars` ( `guild_1` smallint(5) unsigned NOT NULL DEFAULT '0', `guild_2` smallint(5) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`guild_1`,`guild_2`), KEY `guild1` (`guild_1`), KEY `guild2` (`guild_2`))" );
  				m_Command.Enqueue( "CREATE TABLE IF NOT EXISTS `myrunuo_status` ( `char_id` int(10) NOT NULL, PRIMARY KEY (`char_id`))" );
  			}
  			catch ( Exception e )
  			{
  				Console.WriteLine( "MyRunUO: Error creating tables." );
  				Console.WriteLine( e );
  			}
  			if ( m_Command != null )
  				m_Command.Enqueue( null );
  		}
  
  		[Usage( "UpdateMyRunUO" )]
  		[Description( "Starts the process of updating the MyRunUO character and guild database." )]
  		public static void UpdateMyRunUO_OnCommand( CommandEventArgs e )
  		{
  			if ( m_Command != null && m_Command.HasCompleted )
  				m_Command = null;
  
  			if ( m_Timer == null && m_Command == null )
  			{
  				Begin();
  				e.Mobile.SendMessage( "MyRunUO update process has been started." );
  			}
  			else
  			{
  				e.Mobile.SendMessage( "MyRunUO database is already being updated." );
  			}
  		}
  
  		public static void Begin()
  		{
  			if ( m_Command != null && m_Command.HasCompleted )
  				m_Command = null;
  
  			if ( m_Timer != null || m_Command != null )
  				return;
  
  			m_Timer = new MyRunUO();
  			m_Timer.Start();
  		}
  
  		private static Timer m_Timer;
  
  		private Stage m_Stage;
  		private ArrayList m_List;
  		private List<IAccount> m_Collecting;
  		private int m_Index;
  
  		private static DatabaseCommandQueue m_Command;
  
  		private string m_SkillsPath;
  		private string m_LayersPath;
  		private string m_MobilesPath;
  
  		private StreamWriter m_OpSkills;
  		private StreamWriter m_OpLayers;
  		private StreamWriter m_OpMobiles;
  
  		private DateTime m_StartTime;
  
  		public MyRunUO() : base( TimeSpan.FromSeconds( CpuInterval ), TimeSpan.FromSeconds( CpuInterval ) )
  		{
  			m_List = new ArrayList();
  			m_Collecting = new List<IAccount>();
  
  			m_StartTime = DateTime.UtcNow;
  			Console.WriteLine( "MyRunUO: Updating character database" );
  		}
  
  		protected override void OnTick()
  		{
  			bool shouldExit = false;
  
  			try
  			{
  				shouldExit = Process( DateTime.UtcNow + TimeSpan.FromSeconds( CpuInterval * CpuPercent ) );
  
  				if ( shouldExit )
  					Console.WriteLine( "MyRunUO: Database statements compiled in {0:F2} seconds", (DateTime.UtcNow - m_StartTime).TotalSeconds );
  			}
  			catch ( Exception e )
  			{
  				Console.WriteLine( "MyRunUO: {0}: Exception cought while processing", m_Stage );
  				Console.WriteLine( e );
  				shouldExit = true;
  			}
  
  			if ( shouldExit )
  			{
  				m_Command.Enqueue( null );
  
  				Stop();
  				m_Timer = null;
  			}
  		}
  
  		private enum Stage
  		{
  			CollectingMobiles,
  			DumpingMobiles,
  			CollectingGuilds,
  			DumpingGuilds,
  			Complete
  		}
  
  		public bool Process( DateTime endTime )
  		{
  			switch ( m_Stage )
  			{
  				case Stage.CollectingMobiles: CollectMobiles( endTime ); break;
  				case Stage.DumpingMobiles: DumpMobiles( endTime ); break;
  				case Stage.CollectingGuilds: CollectGuilds( endTime ); break;
  				case Stage.DumpingGuilds: DumpGuilds( endTime ); break;
  			}
  
  			return ( m_Stage == Stage.Complete );
  		}
  
  		private static ArrayList m_MobilesToUpdate = new ArrayList();
  
  		public static void QueueMobileUpdate( Mobile m )
  		{
  			if ( !Config.Enabled || Config.LoadDataInFile )
  				return;
  
  			m_MobilesToUpdate.Add( m );
  		}
  
  		public void CollectMobiles( DateTime endTime )
  		{
  			if ( Config.LoadDataInFile )
  			{
  				if ( m_Index == 0 )
  					 m_Collecting.AddRange( Accounts.GetAccounts() );
  
  				for ( int i = m_Index; i < m_Collecting.Count; ++i )
  				{
  					IAccount acct = m_Collecting[i];
  
  					for ( int j = 0; j < acct.Length; ++j )
  					{
  						Mobile mob = acct[j];
  
  						if ( mob != null && mob.AccessLevel < Config.HiddenAccessLevel )
  							m_List.Add( mob );
  					}
  
  					++m_Index;
  
  					if ( DateTime.UtcNow >= endTime )
  						break;
  				}
  
  				if ( m_Index == m_Collecting.Count )
  				{
  					m_Collecting = new List<IAccount>();
  					m_Stage = Stage.DumpingMobiles;
  					m_Index = 0;
  				}
  			}
  			else
  			{
  				m_List = m_MobilesToUpdate;
  				m_MobilesToUpdate = new ArrayList();
  				m_Stage = Stage.DumpingMobiles;
  				m_Index = 0;
  			}
  		}
  
  		public void CheckConnection()
  		{
  			if ( m_Command == null )
  			{
  				m_Command = new DatabaseCommandQueue( "MyRunUO: Characeter database updated in {0:F1} seconds", "MyRunUO Character Database Thread" );
  
  				if ( Config.LoadDataInFile )
  				{
  					m_OpSkills = GetUniqueWriter( "skills", out m_SkillsPath );
  					m_OpLayers = GetUniqueWriter( "layers", out m_LayersPath );
  					m_OpMobiles = GetUniqueWriter( "mobiles", out m_MobilesPath );
  
  					m_Command.Enqueue( "TRUNCATE TABLE myrunuo_characters" );
  					m_Command.Enqueue( "TRUNCATE TABLE myrunuo_characters_layers" );
  					m_Command.Enqueue( "TRUNCATE TABLE myrunuo_characters_skills" );
  				}
  
  				m_Command.Enqueue( "TRUNCATE TABLE myrunuo_guilds" );
  				m_Command.Enqueue( "TRUNCATE TABLE myrunuo_guilds_wars" );
  			}
  		}
  
  		public void ExecuteNonQuery( string text )
  		{
  			m_Command.Enqueue( text );
  		}
  
  		public void ExecuteNonQuery( string format, params string[] args )
  		{
  			ExecuteNonQuery( String.Format( format, args ) );
  		}
  
  		public void ExecuteNonQueryIfNull( string select, string insert )
  		{
  			m_Command.Enqueue( new string[]{ select, insert } );
  		}
  
  		private void AppendCharEntity( string input, int charIndex, ref StringBuilder sb, char c )
  		{
  			if ( sb == null )
  			{
  				if ( charIndex > 0 )
  					sb = new StringBuilder( input, 0, charIndex, input.Length + 20 );
  				else
  					sb = new StringBuilder( input.Length + 20 );
  			}
  
  			sb.Append( "&#" );
  			sb.Append( (int)c );
  			sb.Append( ";" );
  		}
  
  		private void AppendEntityRef( string input, int charIndex, ref StringBuilder sb, string ent )
  		{
  			if ( sb == null )
  			{
  				if ( charIndex > 0 )
  					sb = new StringBuilder( input, 0, charIndex, input.Length + 20 );
  				else
  					sb = new StringBuilder( input.Length + 20 );
  			}
  
  			sb.Append( ent );
  		}
   
  		private string SafeString( string input )
  		{
  			if ( input == null )
  				return "";
  
  			StringBuilder sb = null;
  
  			for ( int i = 0; i < input.Length; ++i )
  			{
  				char c = input[i];
  
  				if ( c < 0x20 || c >= 0x7F )
  				{
  
gt: 					AppendCharEntity( input, i, ref sb, c );