using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Factions;
using Server.Accounting;
using Server.Engines.ConPVP;

namespace Server.Engines.Reports
{
	public class Reports
	{
		public static bool Enabled = false;

		public static void Initialize()
		{
			if ( !Enabled )
				return;

			m_StatsHistory = new SnapshotHistory();
			m_StatsHistory.Load();

			m_StaffHistory = new StaffHistory();
			m_StaffHistory.Load();

			DateTime now = DateTime.Now;

			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 ) );
		}

		private static DateTime m_GenerateTime;

		public static void CheckRegenerate()
		{
			if ( DateTime.Now < m_GenerateTime )
				return;

			Generate();
			m_GenerateTime += TimeSpan.FromHours( 1.0 );
		}

		private static SnapshotHistory m_StatsHistory;
		private static StaffHistory m_StaffHistory;

		public static StaffHistory StaffHistory{ get{ return m_StaffHistory; } }

		public static void Generate()
		{
			Snapshot ss = new Snapshot();

			ss.TimeStamp = DateTime.Now;

			FillSnapshot( ss );

			m_StatsHistory.Snapshots.Add( ss );
			m_StaffHistory.QueueStats.Add( new QueueStatus( Engines.Help.PageQueue.List.Count ) );

			ThreadPool.QueueUserWorkItem( new WaitCallback( UpdateOutput ), ss );
		}

		private static void UpdateOutput( object state )
		{
			m_StatsHistory.Save();
			m_StaffHistory.Save();

			HtmlRenderer renderer = new HtmlRenderer( "stats", (Snapshot) state, m_StatsHistory );
			renderer.Render();
			renderer.Upload();

			renderer = new HtmlRenderer( "staff", m_StaffHistory );
			renderer.Render();
			renderer.Upload();
		}

		public static void FillSnapshot( Snapshot ss )
		{
			ss.Children.Add( CompileGeneralStats() );
			ss.Children.Add( CompilePCByDL() );
			ss.Children.Add( CompileTop15() );
			ss.Children.Add( CompileDislikedArenas() );
			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;
		}

		private static Chart CompilePCByDL()
		{
			BarGraph chart = new BarGraph( "Player Count By Dueling Level", "graphs_pc_by_dl", 5, "Dueling Level", "Players", BarGraphRenderMode.Bars );

			int lastLevel = -1;
			ChartItem lastItem = null;

			Ladder ladder = Ladder.Instance;

			if ( ladder != null )
			{
				ArrayList entries = ladder.ToArrayList();

				for ( int i = entries.Count - 1; i >= 0; --i )
				{
					LadderEntry entry = (LadderEntry)entries[i];
					int level = Ladder.GetLevel( entry.Experience );

					if ( lastItem == null || level != lastLevel )
					{
						chart.Items.Add( lastItem = new ChartItem( level.ToString(), 1 ) );
						lastLevel = level;
					}
					else
					{
						lastItem.Value++;
					}
				}
			}

			return chart;
		}

		private static Report CompileTop15()
		{
			Report report = new Report( "Top 15 Duelists", "80%" );

			report.Columns.Add( "6%", "center", "Rank" );
			report.Columns.Add( "6%", "center", "Level" );
			report.Columns.Add( "6%", "center", "Guild" );
			report.Columns.Add( "70%", "left", "Name" );
			report.Columns.Add( "6%", "center", "Wins" );
			report.Columns.Add( "6%", "center", "Losses" );

			Ladder ladder = Ladder.Instance;

			if ( ladder != null )
			{
				ArrayList entries = ladder.ToArrayList();

				for ( int i = 0; i < entries.Count && i < 15; ++i )
				{
					LadderEntry entry = (LadderEntry)entries[i];
					int level = Ladder.GetLevel( entry.Experience );
					string guild = "";

					if ( entry.Mobile.Guild != null )
						guild = entry.Mobile.Guild.Abbreviation;

					ReportItem item = new ReportItem();

					item.Values.Add( LadderGump.Rank( entry.Index + 1 ) );
					item.Values.Add( level.ToString(), "N0" );
					item.Values.Add( guild );
					item.Values.Add( entry.Mobile.Name );
					item.Values.Add( entry.Wins.ToString(), "N0" );
					item.Values.Add( entry.Losses.ToString(), "N0" );

					report.Items.Add( item );
				}
			}

			return report;
		}

		private static Chart CompileDislikedArenas()
		{
			PieChart chart = new PieChart( "Most Disliked Arenas", "graphs_arenas_disliked", false );

			Preferences prefs = Preferences.Instance;

			if ( prefs != null )
			{
				List<Arena> arenas = Arena.Arenas;

				for ( int i = 0; i < arenas.Count; ++i )
				{
					Arena arena = arenas[i];

					string name = arena.Name;

					if ( name != null )
						chart.Items.Add( name, 0 );
				}

				ArrayList entries = prefs.Entries;

				for ( int i = 0; i < entries.Count; ++i )
				{
					PreferencesEntry entry = (PreferencesEntry)entries[i];
					ArrayList list = entry.Disliked;

					for ( int j = 0; j < list.Count; ++j )
					{
						string disliked = (string)list[j];

						for ( int k = 0; k < chart.Items.Count; ++k )
						{
							ChartItem item = chart.Items[k];

							if ( item.Name == disliked )
							{
								++item.Value;
								break;
							}
						}
					}
				}
			}

			return chart;
		}

		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 class SkillDistribution : IComparable
		{
			public SkillInfo m_Skill;
			public int m_NumberOfGMs;

			public SkillDistribution( SkillInfo skill )
			{
				m_Skill = skill;
			}

			public int CompareTo( object obj )
			{
				return ( ((SkillDistribution)obj).m_NumberOfGMs - m_NumberOfGMs );
			}
		}

		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 <= 7200 && 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 )
			{
				Town town = towns[i];

				string prices = "Normal";

				if ( town.Tax < 0 )
					prices = town.Tax.ToString() + "%";
				else if ( town.Tax > 0 )
					prices = "+" + town.Tax.ToString() + "%";

				ReportItem item = new ReportItem();

				item.Values.Add( town.Definition.FriendlyName );
				item.Values.Add( town.Owner == null ? "Neutral" : town.Owner.Definition.FriendlyName );
				item.Values.Add( town.Sheriff == null ? "" : town.Sheriff.Name );
				item.Values.Add( town.Finance == null ? "" : town.Finance.Name );
				item.Values.Add( town.Silver.ToString(), "N0" );
				item.Values.Add( prices );

				report.Items.Add( item );
			}

			return report;
		}
	}
}
