context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Threading;
  using Server.Accounting;
  using Server.Commands;
  using Server.Factions;
  using Server.Mobiles;
  using Server.Network;
  
  namespace Server.Engines.Reports
  {
      public class Reports
      {
          public static bool Enabled = Config.Get("Reports.AutoGenerate", false);
          private static DateTime m_GenerateTime;
          private static SnapshotHistory m_StatsHistory;
          private static StaffHistory m_StaffHistory;
          public static StaffHistory StaffHistory
          {
              get
              {
                  return m_StaffHistory;
              }
          }
          public static void Initialize()
          {
              CommandSystem.Register("GenReports", AccessLevel.Administrator, new CommandEventHandler(GenReports_OnCommand));
  
              m_StatsHistory = new SnapshotHistory();
              m_StatsHistory.Load();
  
              m_StaffHistory = new StaffHistory();
              m_StaffHistory.Load();
  
              DateTime now = DateTime.UtcNow;
              
              if (!Enabled)
                  return;
  
              DateTime date = now.Date;
              TimeSpan timeOfDay = now.TimeOfDay;
  
              m_GenerateTime = date + TimeSpan.FromHours(Math.Ceiling(timeOfDay.TotalHours));
  
              Timer.DelayCall(TimeSpan.FromMinutes(0.5), TimeSpan.FromMinutes(0.5), new TimerCallback(CheckRegenerate));
          }
  
          public static void CheckRegenerate()
          {
              if (DateTime.UtcNow < m_GenerateTime)
                  return;
  
              Generate();
              m_GenerateTime += TimeSpan.FromHours(1.0);
          }
          
          [Usage("GenReports")]
          [Description("Generates Reports on Command.")]
          public static void GenReports_OnCommand(CommandEventArgs e)
          {
  			Generate();
          }
  
          public static void Generate()
          {
              Snapshot ss = new Snapshot();
  
              ss.TimeStamp = DateTime.UtcNow;
  
              FillSnapshot(ss);
  
              m_StatsHistory.Snapshots.Add(ss);
              m_StaffHistory.QueueStats.Add(new QueueStatus(Engines.Help.PageQueue.List.Count));
  
              ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateOutput), ss);
          }
  
          public static void FillSnapshot(Snapshot ss)
          {
              ss.Children.Add(CompileGeneralStats());
              ss.Children.Add(CompileStatChart());
  
              PersistableObject[] obs = CompileSkillReports();
  
              for (int i = 0; i < obs.Length; ++i)
                  ss.Children.Add(obs[i]);
  
              obs = CompileFactionReports();
  
              for (int i = 0; i < obs.Length; ++i)
                  ss.Children.Add(obs[i]);
          }
  
          public static Report CompileGeneralStats()
          {
              Report report = new Report("General Stats", "200");
  
              report.Columns.Add("50%", "left");
              report.Columns.Add("50%", "left");
  
              int npcs = 0, players = 0;
  
              foreach (Mobile mob in World.Mobiles.Values)
              {
                  if (mob.Player)
                      ++players;
                  else
                      ++npcs;
              }
  
              report.Items.Add("NPCs", npcs, "N0");
              report.Items.Add("Players", players, "N0");
              report.Items.Add("Clients", NetState.Instances.Count, "N0");
              report.Items.Add("Accounts", Accounts.Count, "N0");
              report.Items.Add("Items", World.Items.Count, "N0");
  
              return report;
          }
  
          public static Chart CompileStatChart()
          {
              PieChart chart = new PieChart("Stat Distribution", "graphs_strdexint_distrib", true);
  
              ChartItem strItem = new ChartItem("Strength", 0);
              ChartItem dexItem = new ChartItem("Dexterity", 0);
              ChartItem intItem = new ChartItem("Intelligence", 0);
  
              foreach (Mobile mob in World.Mobiles.Values)
              {
                  if (mob.RawStatTotal == mob.StatCap && mob is PlayerMobile)
                  {
                      strItem.Value += mob.RawStr;
                      dexItem.Value += mob.RawDex;
                      intItem.Value += mob.RawInt;
                  }
              }
  
              chart.Items.Add(strItem);
              chart.Items.Add(dexItem);
              chart.Items.Add(intItem);
  
              return chart;
          }
  
          public static SkillDistribution[] GetSkillDistribution()
          {
              int skip = (Core.ML ? 0 : Core.SE ? 1 : Core.AOS ? 3 : 6);
  
              SkillDistribution[] distribs = new SkillDistribution[SkillInfo.Table.Length - skip];
  
              for (int i = 0; i < distribs.Length; ++i)
                  distribs[i] = new SkillDistribution(SkillInfo.Table[i]);
  
              foreach (Mobile mob in World.Mobiles.Values)
              {
                  if (mob.SkillsTotal >= 1500 && mob.SkillsTotal <= (Config.Get("PlayerCaps.TotalSkillCap", 7000) + 200) && mob is PlayerMobile)
                  {
                      Skills skills = mob.Skills;
  
                      for (int i = 0; i < skills.Length - skip; ++i)
                      {
                          Skill skill = skills[i];
  
                          if (skill.BaseFixedPoint >= 1000)
                              distribs[i].m_NumberOfGMs++;
                      }
                  }
              }
  
              return distribs;
          }
  
          public static PersistableObject[] CompileSkillReports()
          {
              SkillDistribution[] distribs = GetSkillDistribution();
  
              Array.Sort(distribs);
  
              return new PersistableObject[] { CompileSkillChart(distribs), CompileSkillReport(distribs) };
          }
  
          public static Report CompileSkillReport(SkillDistribution[] distribs)
          {
              Report report = new Report("Skill Report", "300");
  
              report.Columns.Add("70%", "left", "Name");
              report.Columns.Add("30%", "center", "GMs");
  
              for (int i = 0; i < distribs.Length; ++i)
                  report.Items.Add(distribs[i].m_Skill.Name, distribs[i].m_NumberOfGMs, "N0");
  
              return report;
          }
  
          public static Chart CompileSkillChart(SkillDistribution[] distribs)
          {
              PieChart chart = new PieChart("GM Skill Distribution", "graphs_skill_distrib", true);
  
              for (int i = 0; i < 12; ++i)
                  chart.Items.Add(distribs[i].m_Skill.Name, distribs[i].m_NumberOfGMs);
  
              int rem = 0;
  
              for (int i = 12; i < distribs.Length; ++i)
                  rem += distribs[i].m_NumberOfGMs;
  
              chart.Items.Add("Other", rem);
  
              return chart;
          }
  
          public static PersistableObject[] CompileFactionReports()
          {
              return new PersistableObject[] { CompileFactionMembershipChart(), CompileFactionReport(), CompileFactionTownReport(), CompileSigilReport(), CompileFactionLeaderboard() };
          }
  
          public static Chart CompileFactionMembershipChart()
          {
              PieChart chart = new PieChart("Faction Membership", "graphs_faction_membership", true);
  
              List<Faction> factions = Faction.Factions;
  
              for (int i = 0; i < factions.Count; ++i)
                  chart.Items.Add(factions[i].Definition.FriendlyName, factions[i].Members.Count);
  
              return chart;
          }
  
          public static Report CompileFactionLeaderboard()
          {
              Report report = new Report("Faction Leaderboard", "60%");
  
              report.Columns.Add("28%", "center", "Name");
              report.Columns.Add("28%", "center", "Faction");
              report.Columns.Add("28%", "center", "Office");
              report.Columns.Add("16%", "center", "Kill Points");
  
              ArrayList list = new ArrayList();
  
              List<Faction> factions = Faction.Factions;
  
              for (int i = 0; i < factions.Count; ++i)
              {
                  Faction faction = factions[i];
  
                  list.AddRange(faction.Members);
              }
  
              list.Sort();
              list.Reverse();
  
              for (int i = 0; i < list.Count && i < 15; ++i)
              {
                  PlayerState pl = (PlayerState)list[i];
  
                  string office;
  
                  if (pl.Faction.Commander == pl.Mobile)
                      office = "Commanding Lord";
                  else if (pl.Finance != null)
                      office = String.Format("{0} Finance Minister", pl.Finance.Definition.FriendlyName);
                  else if (pl.Sheriff != null)
                      office = String.Format("{0} Sheriff", pl.Sheriff.Definition.FriendlyName);
                  else
                      office = "";
  
                  ReportItem item = new ReportItem();
  
                  item.Values.Add(pl.Mobile.Name);
                  item.Values.Add(pl.Faction.Definition.FriendlyName);
                  item.Values.Add(office);
                  item.Values.Add(pl.KillPoints.ToString(), "N0");
  
                  report.Items.Add(item);
              }
  
              return report;
          }
  
          public static Report CompileFactionReport()
          {
              Report report = new Report("Faction Statistics", "80%");
  
              report.Columns.Add("20%", "center", "Name");
              report.Columns.Add("20%", "center", "Commander");
              report.Columns.Add("15%", "center", "Members");
              report.Columns.Add("15%", "center", "Merchants");
              report.Columns.Add("15%", "center", "Kill Points");
              report.Columns.Add("15%", "center", "Silver");
  
              List<Faction> factions = Faction.Factions;
  
              for (int i = 0; i < factions.Count; ++i)
              {
                  Faction faction = factions[i];
                  List<PlayerState> members = faction.Members;
  
                  int totalKillPoints = 0;
                  int totalMerchants = 0;
  
                  for (int j = 0; j < members.Count; ++j)
                  {
                      totalKillPoints += members[j].KillPoints;
  
                      if (members[j].MerchantTitle != MerchantTitle.None)
                          ++totalMerchants;
                  }
  
                  ReportItem item = new ReportItem();
  
                  item.Values.Add(faction.Definition.FriendlyName);
                  item.Values.Add(faction.Commander == null ? "" : faction.Commander.Name);
                  item.Values.Add(faction.Members.Count.ToString(), "N0");
                  item.Values.Add(totalMerchants.ToString(), "N0");
                  item.Values.Add(totalKillPoints.ToString(), "N0");
                  item.Values.Add(faction.Silver.ToString(), "N0");
  
                  report.Items.Add(item);
              }
  
              return report;
          }
  
          public static Report CompileSigilReport()
          {
              Report report = new Report("Faction Town Sigils", "50%");
  
              report.Columns.Add("35%", "center", "Town");
              report.Columns.Add("35%", "center", "Controller");
              report.Columns.Add("30%", "center", "Capturable");
  
              List<Sigil> sigils = Sigil.Sigils;
  
              for (int i = 0; i < sigils.Count; ++i)
              {
                  Sigil sigil = sigils[i];
  
                  string controller = "Unknown";
  
                  Mobile mob = sigil.RootParent as Mobile;
  
                  if (mob != null)
                  {
                      Faction faction = Faction.Find(mob);
  
                      if (faction != null)
                          controller = faction.Definition.FriendlyName;
                  }
                  else if (sigil.LastMonolith != null && sigil.LastMonolith.Faction != null)
                  {
                      controller = sigil.LastMonolith.Faction.Definition.FriendlyName;
                  }
  
                  ReportItem item = new ReportItem();
  
                  item.Values.Add(sigil.Town == null ? "" : sigil.Town.Definition.FriendlyName);
                  item.Values.Add(controller);
                  item.Values.Add(sigil.IsPurifying ? "No" : "Yes");
  
                  report.Items.Add(item);
              }
  
              return report;
          }
  
          public static Report CompileFactionTownReport()
          {
              Report report = new Report("Faction Towns", "80%");
  
              report.Columns.Add("20%", "center", "Name");
              report.Columns.Add("20%", "center", "Owner");
              report.Columns.Add("17%", "center", "Sheriff");
              report.Columns.Add("17%", "center", "Finance Minister");
              report.Columns.Add("13%", "center", "Silver");
              report.Columns.Add("13%", "center", "Prices");
  
              List<Town> towns = Town.Towns;
  
              for (int i = 0; i < towns.Count; ++i)
              {
  
gt:                 Town town = towns[i];