context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Text;
  using Server.Accounting;
  using Server.Commands;
  using Server.Guilds;
  using Server.Misc;
  using Server.Mobiles;
  
  namespace Server.Engines.MyRunUO
  {
      public class MyRunUO : Timer
      {
          public const char LineStart = '\"';
          public const string EntrySep = "\",\"";
          public const string LineEnd = "\"\n";
          private static readonly double CpuInterval = 0.1;// Processor runs every 0.1 seconds
          private static readonly double CpuPercent = 0.25;// Processor runs for 25% of Interval, or ~25ms. This should take around 25% cpu
          private static Timer m_Timer;
          private static DatabaseCommandQueue m_Command;
          private static ArrayList m_MobilesToUpdate = new ArrayList();
          private readonly DateTime m_StartTime;
          private readonly ArrayList m_Items = new ArrayList();
          private Stage m_Stage;
          private ArrayList m_List;
          private List<IAccount> m_Collecting;
          private int m_Index;
          private string m_SkillsPath;
          private string m_LayersPath;
          private string m_MobilesPath;
          private StreamWriter m_OpSkills;
          private StreamWriter m_OpLayers;
          private StreamWriter m_OpMobiles;
  	
          public MyRunUO()
              : base(TimeSpan.FromSeconds(CpuInterval), TimeSpan.FromSeconds(CpuInterval))
          {
              this.m_List = new ArrayList();
              this.m_Collecting = new List<IAccount>();
  
              this.m_StartTime = DateTime.UtcNow;
              Console.WriteLine("MyRunUO: Updating character database");
          }
  
          private enum Stage
          {
              CollectingMobiles,
              DumpingMobiles,
              CollectingGuilds,
              DumpingGuilds,
              Complete
          }
          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();
          }
  
          public static void QueueMobileUpdate(Mobile m)
          {
              if (!Config.Enabled || Config.LoadDataInFile)
                  return;
  
              m_MobilesToUpdate.Add(m);
          }
  
          public bool Process(DateTime endTime)
          {
              switch ( this.m_Stage )
              {
                  case Stage.CollectingMobiles:
                      this.CollectMobiles(endTime);
                      break;
                  case Stage.DumpingMobiles:
                      this.DumpMobiles(endTime);
                      break;
                  case Stage.CollectingGuilds:
                      this.CollectGuilds(endTime);
                      break;
                  case Stage.DumpingGuilds:
                      this.DumpGuilds(endTime);
                      break;
              }
  
              return (this.m_Stage == Stage.Complete);
          }
  
          public void CollectMobiles(DateTime endTime)
          {
              if (Config.LoadDataInFile)
              {
                  if (this.m_Index == 0)
                      this.m_Collecting.AddRange(Accounts.GetAccounts());
  
                  for (int i = this.m_Index; i < this.m_Collecting.Count; ++i)
                  {
                      IAccount acct = this.m_Collecting[i];
  
                      for (int j = 0; j < acct.Length; ++j)
                      {
                          Mobile mob = acct[j];
  
                          if (mob != null && mob.AccessLevel < Config.HiddenAccessLevel)
                              this.m_List.Add(mob);
                      }
  
                      ++this.m_Index;
  
                      if (DateTime.UtcNow >= endTime)
                          break;
                  }
  
                  if (this.m_Index == this.m_Collecting.Count)
                  {
                      this.m_Collecting = new List<IAccount>();
                      this.m_Stage = Stage.DumpingMobiles;
                      this.m_Index = 0;
                  }
              }
              else
              {
                  this.m_List = m_MobilesToUpdate;
                  m_MobilesToUpdate = new ArrayList();
                  this.m_Stage = Stage.DumpingMobiles;
                  this.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)
                  {
                      this.m_OpSkills = this.GetUniqueWriter("skills", out this.m_SkillsPath);
                      this.m_OpLayers = this.GetUniqueWriter("layers", out this.m_LayersPath);
                      this.m_OpMobiles = this.GetUniqueWriter("mobiles", out this.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)
          {
              this.ExecuteNonQuery(String.Format(format, args));
          }
  
          public void ExecuteNonQueryIfNull(string select, string insert)
          {
              m_Command.Enqueue(new string[] { select, insert });
          }
  
          public void InsertMobile(Mobile mob)
          {
              string guildTitle = mob.GuildTitle;
  
              if (guildTitle == null || (guildTitle = guildTitle.Trim()).Length == 0)
                  guildTitle = "NULL";
              else
                  guildTitle = this.SafeString(guildTitle);
  
              string notoTitle = this.SafeString(Titles.ComputeTitle(null, mob));
              string female = (mob.Female ? "1" : "0");
  			
              bool pubBool = (mob is PlayerMobile) && (((PlayerMobile)mob).PublicMyRunUO);
  
              string pubString = (pubBool ? "1" : "0");
  
              string guildId = (mob.Guild == null ? "NULL" : mob.Guild.Id.ToString());
  
              if (Config.LoadDataInFile)
              {
                  this.m_OpMobiles.Write(LineStart);
                  this.m_OpMobiles.Write(mob.Serial.Value);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(this.SafeString(mob.Name));
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(mob.RawStr);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(mob.RawDex);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(mob.RawInt);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(female);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(mob.Kills);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(guildId);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(guildTitle);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(notoTitle);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(mob.Hue);
                  this.m_OpMobiles.Write(EntrySep);
                  this.m_OpMobiles.Write(pubString);
                  this.m_OpMobiles.Write(LineEnd);
              }
              else
              {
                  this.ExecuteNonQuery("INSERT INTO myrunuo_characters (char_id, char_name, char_str, char_dex, char_int, char_female, char_counts, char_guild, char_guildtitle, char_nototitle, char_bodyhue, char_public ) VALUES ({0}, '{1}', {2}, {3}, {4}, {5}, {6}, {7}, {8}, '{9}', {10}, {11})", mob.Serial.Value.ToString(), this.SafeString(mob.Name), mob.RawStr.ToString(), mob.RawDex.ToString(), mob.RawInt.ToString(), female, mob.Kills.ToString(), guildId, guildTitle, notoTitle, mob.Hue.ToString(), pubString);
              }
          }
  
          public void InsertSkills(Mobile mob)
          {
              Skills skills = mob.Skills;
              string serial = mob.Serial.Value.ToString();
  
  
gt:             for (int i = 0; i < skills.Length; ++i)