using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net.Mail;
using Server.Accounting;
using Server.Commands;
using Server.Engines.Reports;
using Server.Misc;
using Server.Mobiles;
using Server.Network;

namespace Server.Engines.Help
{
	public enum PageType
	{
		Bug,
		Stuck,
		Account,
		Question,
		Suggestion,
		Other,
		VerbalHarassment,
		PhysicalHarassment
	}

	public class PageEntry
	{
		// What page types should have a speech log as attachment?
		public static readonly PageType[] SpeechLogAttachment = new PageType[]
			{
				PageType.VerbalHarassment
			};

		private readonly Mobile m_Sender;
		private Mobile m_Handler;
		private readonly DateTime m_Sent;
		private readonly string m_Message;
		private readonly PageType m_Type;
		private readonly Point3D m_PageLocation;
		private readonly Map m_PageMap;
		private readonly List<SpeechLogEntry> m_SpeechLog;

		private readonly PageInfo m_PageInfo;

		public PageInfo PageInfo
		{
			get{ return m_PageInfo; }
		}

		public Mobile Sender
		{
			get
			{
				return m_Sender;
			}
		}

		public Mobile Handler
		{
			get
			{
				return m_Handler;
			}
			set
			{
				PageQueue.OnHandlerChanged( m_Handler, value, this );
				m_Handler = value;
			}
		}

		public DateTime Sent
		{
			get
			{
				return m_Sent;
			}
		}

		public string Message
		{
			get
			{
				return m_Message;
			}
		}

		public PageType Type
		{
			get
			{
				return m_Type;
			}
		}

		public Point3D PageLocation
		{
			get
			{
				return m_PageLocation;
			}
		}

		public Map PageMap
		{
			get
			{
				return m_PageMap;
			}
		}

		public List<SpeechLogEntry> SpeechLog
		{
			get
			{
				return m_SpeechLog;
			}
		}

		private Timer m_Timer;

		public void Stop()
		{
			if ( m_Timer != null )
				m_Timer.Stop();

			m_Timer = null;
		}

		public void AddResponse( Mobile mob, string text )
		{
			if ( m_PageInfo != null )
			{
				lock ( m_PageInfo )
					m_PageInfo.Responses.Add( PageInfo.GetAccount( mob ), text );

				if ( PageInfo.ResFromResp( text ) != PageResolution.None )
					m_PageInfo.UpdateResolver();
			}
		}

		public PageEntry( Mobile sender, string message, PageType type )
		{
			m_Sender = sender;
			m_Sent = DateTime.Now;
			m_Message = Utility.FixHtml( message );
			m_Type = type;
			m_PageLocation = sender.Location;
			m_PageMap = sender.Map;

			PlayerMobile pm = sender as PlayerMobile;
			if ( pm != null && pm.SpeechLog != null && Array.IndexOf( SpeechLogAttachment, type ) >= 0 )
				m_SpeechLog = new List<SpeechLogEntry>( pm.SpeechLog );

			m_Timer = new InternalTimer( this );
			m_Timer.Start();

			StaffHistory history = Reports.Reports.StaffHistory;

			if ( history != null )
			{
				m_PageInfo = new PageInfo( this );

				history.AddPage( m_PageInfo );
			}
		}

		private class InternalTimer : Timer
		{
			private static readonly TimeSpan StatusDelay = TimeSpan.FromMinutes( 2.0 );

			private readonly PageEntry m_Entry;

			public InternalTimer( PageEntry entry ) : base( TimeSpan.FromSeconds( 1.0 ), StatusDelay )
			{
				m_Entry = entry;
			}

			protected override void OnTick()
			{
				int index = PageQueue.IndexOf( m_Entry );

                if (m_Entry.Sender.NetState != null && index != -1)
                {
                    m_Entry.Sender.SendLocalizedMessage(1008077, false, (index + 1).ToString());// Thank you for paging. Queue status : 
                    m_Entry.Sender.SendMessage("You can reference our website at www.in-uo.net. To cancel your page, please select the help button again and select cancel.");

                    if (m_Entry.Handler != null && m_Entry.Handler.NetState == null)
                    {
                        m_Entry.Handler = null;
                    }
                }
/* Don't delete pages on logout
				else
				{
					PageQueue.Remove( m_Entry );
				}
*/
			}
		}
	}

	public class PageQueue
	{
		private static readonly ArrayList m_List = new ArrayList();
		private static readonly Hashtable m_KeyedByHandler = new Hashtable();
		private static readonly Hashtable m_KeyedBySender = new Hashtable();

		public static void Initialize()
		{
			CommandSystem.Register( "Pages", AccessLevel.Counselor, Pages_OnCommand );
		}

		public static bool CheckAllowedToPage( Mobile from )
		{
			PlayerMobile pm = from as PlayerMobile;

			if ( pm == null )
				return true;

			if ( pm.DesignContext != null )
			{
				from.SendLocalizedMessage( 500182 ); // You cannot request help while customizing a house or transferring a character.
				return false;
			}
			else if ( pm.PagingSquelched )
			{
				from.SendMessage( "You cannot request help, sorry." );
				return false;
			}

			return true;
		}

		public static string GetPageTypeName( PageType type )
		{
			if ( type == PageType.VerbalHarassment )
				return "Verbal Harassment";
			else if ( type == PageType.PhysicalHarassment )
				return "Physical Harassment";
			else
				return type.ToString();
		}

		public static void OnHandlerChanged( Mobile old, Mobile value, PageEntry entry )
		{
			if ( old != null )
				m_KeyedByHandler.Remove( old );

			if ( value != null )
				m_KeyedByHandler[value] = entry;
		}

		[Usage( "Pages" )]
		[Description( "Opens the page queue menu." )]
		private static void Pages_OnCommand( CommandEventArgs e )
		{
			PageEntry entry = (PageEntry)m_KeyedByHandler[e.Mobile];

			if ( entry != null )
			{
				e.Mobile.SendGump( new PageEntryGump( e.Mobile, entry ) );
			}
			else if ( m_List.Count > 0 )
			{
				e.Mobile.SendGump( new PageQueueGump() );
			}
			else
			{
				e.Mobile.SendMessage( "The page queue is empty." );
			}
		}

		public static bool IsHandling( Mobile check )
		{
			return m_KeyedByHandler.ContainsKey( check );
		}

		public static bool Contains( Mobile sender )
		{
			return m_KeyedBySender.ContainsKey( sender );
		}

		public static int IndexOf( PageEntry e )
		{
			return m_List.IndexOf( e );
		}

		public static void Cancel( Mobile sender )
		{
			Remove( (PageEntry) m_KeyedBySender[sender] );
		}

		public static void Remove( PageEntry e )
		{
			if ( e == null )
				return;

			e.Stop();

			m_List.Remove( e );
			m_KeyedBySender.Remove( e.Sender );

			if ( e.Handler != null )
				m_KeyedByHandler.Remove( e.Handler );
		}

		public static PageEntry GetEntry( Mobile sender )
		{
			return (PageEntry)m_KeyedBySender[sender];
		}

		public static void Remove( Mobile sender )
		{
			Remove( GetEntry( sender ) );
		}

		public static ArrayList List
		{
			get
			{
				return m_List;
			}
		}

		public static void Enqueue( PageEntry entry )
		{
			m_List.Add( entry );
			m_KeyedBySender[entry.Sender] = entry;

			bool isStaffOnline = false;

			foreach ( NetState ns in NetState.Instances )
			{
				Mobile m = ns.Mobile;

				if ( m != null && m.AccessLevel >= AccessLevel.Counselor && m.AutoPageNotify && !IsHandling( m ) )
					m.SendMessage( "A new page has been placed in the queue." );

				if ( m != null && m.AccessLevel >= AccessLevel.Counselor && m.AutoPageNotify && m.LastMoveTime >= (DateTime.Now - TimeSpan.FromMinutes( 10.0 )) )
					isStaffOnline = true;
			}

			if ( !isStaffOnline )
				entry.Sender.SendMessage( "We are sorry, but no staff members are currently available to assist you.  Your page will remain in the queue until one becomes available, or until you cancel it manually." );

			if ( Email.SpeechLogPageAddresses != null && entry.SpeechLog != null )
				SendEmail( entry );
		}

		private static void SendEmail( PageEntry entry )
		{
			Mobile sender = entry.Sender;
			DateTime time = DateTime.Now;

			MailMessage mail = new MailMessage( "RunUO", Email.SpeechLogPageAddresses );

			mail.Subject = "RunUO Speech Log Page Forwarding";

			using ( StringWriter writer = new StringWriter() )
			{
				writer.WriteLine( "RunUO Speech Log Page - {0}", GetPageTypeName( entry.Type ) );
				writer.WriteLine();

				writer.WriteLine( "From: '{0}', Account: '{1}'", sender.RawName, sender.Account is Account ? sender.Account.Username : "???" );
				writer.WriteLine( "Location: {0} [{1}]", sender.Location, sender.Map );
				writer.WriteLine( "Sent on: {0}/{1:00}/{2:00} {3}:{4:00}:{5:00}", time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second );
				writer.WriteLine();

				writer.WriteLine( "Message:" );
				writer.WriteLine( "'{0}'", entry.Message );
				writer.WriteLine();

				writer.WriteLine( "Speech Log" );
				writer.WriteLine( "==========" );

				foreach ( SpeechLogEntry logEntry in entry.SpeechLog )
				{
					Mobile from = logEntry.From;
					string fromName = from.RawName;
					string fromAccount = from.Account is Account ? from.Account.Username : "???";
					DateTime created = logEntry.Created;
					string speech = logEntry.Speech;

					writer.WriteLine( "{0}:{1:00}:{2:00} - {3} ({4}): '{5}'", created.Hour, created.Minute, created.Second, fromName, fromAccount, speech );
				}

				mail.Body = writer.ToString();
			}

			Email.AsyncSend( mail );
		}
	}
}