using System;
using System.Collections;
using Server;

namespace Server.Engines.Mahjong
{
	public class MahjongPlayers
	{
		private MahjongGame m_Game;
		private Mobile[] m_Players;
		private bool[] m_InGame;
		private bool[] m_PublicHand;
		private int[] m_Scores;
		private int m_DealerPosition;
		private ArrayList m_Spectators;

		public MahjongGame Game { get { return m_Game; } }
		public int Seats { get { return m_Players.Length; } }
		public Mobile Dealer { get { return m_Players[m_DealerPosition]; } }
		public int DealerPosition { get { return m_DealerPosition; } }

		public MahjongPlayers( MahjongGame game, int maxPlayers, int baseScore )
		{
			m_Game = game;
			m_Spectators = new ArrayList();

			m_Players = new Mobile[maxPlayers];
			m_InGame = new bool[maxPlayers];
			m_PublicHand = new bool[maxPlayers];
			m_Scores = new int[maxPlayers];

			for ( int i = 0; i < m_Scores.Length; i++ )
				m_Scores[i] = baseScore;
		}

		public Mobile GetPlayer( int index )
		{
			if ( index < 0 || index >= m_Players.Length )
				return null;
			else
				return m_Players[index];
		}

		public int GetPlayerIndex( Mobile mobile )
		{
			for ( int i = 0; i < m_Players.Length; i++ )
			{
				if ( m_Players[i] == mobile )
					return i;
			}
			return -1;
		}

		public bool IsInGameDealer( Mobile mobile )
		{
			if ( Dealer != mobile )
				return false;
			else
				return m_InGame[m_DealerPosition];
		}

		public bool IsInGamePlayer( int index )
		{
			if ( index < 0 || index >= m_Players.Length || m_Players[index] == null )
				return false;
			else
				return m_InGame[index];
		}

		public bool IsInGamePlayer( Mobile mobile )
		{
			int index = GetPlayerIndex( mobile );

			return IsInGamePlayer( index );
		}

		public bool IsSpectator( Mobile mobile )
		{
			return m_Spectators.Contains( mobile );
		}

		public int GetScore( int index )
		{
			if ( index < 0 || index >= m_Scores.Length )
				return 0;
			else
				return m_Scores[index];
		}

		public bool IsPublic( int index )
		{
			if ( index < 0 || index >= m_PublicHand.Length )
				return false;
			else
				return m_PublicHand[index];
		}

		public void SetPublic( int index, bool value )
		{
			if ( index < 0 || index >= m_PublicHand.Length || m_PublicHand[index] == value )
				return;

			m_PublicHand[index] = value;

			SendTilesPacket( true, !m_Game.SpectatorVision );

			if ( IsInGamePlayer( index ) )
				m_Players[index].SendLocalizedMessage( value ? 1062775 : 1062776 ); // Your hand is [not] publicly viewable.
		}

		public ArrayList GetInGameMobiles( bool players, bool spectators )
		{
			ArrayList list = new ArrayList();

			if ( players )
			{
				for ( int i = 0; i < m_Players.Length; i++ )
				{
					if ( IsInGamePlayer( i ) )
						list.Add( m_Players[i] );
				}
			}

			if ( spectators )
			{
				list.AddRange( m_Spectators );
			}

			return list;
		}

		public void CheckPlayers()
		{
			bool removed = false;

			for ( int i = 0; i < m_Players.Length; i++ )
			{
				Mobile player = m_Players[i];

				if ( player != null )
				{
					if ( player.Deleted )
					{
						m_Players[i] = null;

						SendPlayerExitMessage( player );
						UpdateDealer( true );

						removed = true;
					}
					else if ( m_InGame[i] )
					{
						if ( player.NetState == null )
						{
							m_InGame[i] = false;

							SendPlayerExitMessage( player );
							UpdateDealer( true );

							removed = true;
						}
						else if ( !m_Game.IsAccessibleTo( player ) || player.Map != m_Game.Map || !player.InRange( m_Game.GetWorldLocation(), 5 ) )
						{
							m_InGame[i] = false;

							player.Send( new MahjongRelieve( m_Game ) );

							SendPlayerExitMessage( player );
							UpdateDealer( true );

							removed = true;
						}
					}
				}
			}

			for ( int i = 0; i < m_Spectators.Count; )
			{
				Mobile mobile = (Mobile)m_Spectators[i];

				if ( mobile.NetState == null || mobile.Deleted )
				{
					m_Spectators.RemoveAt( i );
				}
				else if ( !m_Game.IsAccessibleTo( mobile ) || mobile.Map != m_Game.Map || !mobile.InRange( m_Game.GetWorldLocation(), 5 ) )
				{
					m_Spectators.RemoveAt( i );

					mobile.Send( new MahjongRelieve( m_Game ) );
				}
				else
				{
					i++;
				}
			}

			if ( removed && !UpdateSpectators() )
				SendPlayersPacket( true, true );
		}

		private void UpdateDealer( bool message )
		{
			if ( IsInGamePlayer( m_DealerPosition ) )
				return;

			for ( int i = m_DealerPosition + 1; i < m_Players.Length; i++ )
			{
				if ( IsInGamePlayer( i ) )
				{
					m_DealerPosition = i;

					if ( message )
						SendDealerChangedMessage();

					return;
				}
			}

			for ( int i = 0; i < m_DealerPosition; i++ )
			{
				if ( IsInGamePlayer( i ) )
				{
					m_DealerPosition = i;

					if ( message )
						SendDealerChangedMessage();

					return;
				}
			}
		}

		private int GetNextSeat()
		{
			for ( int i = m_DealerPosition; i < m_Players.Length; i++ )
			{
				if ( m_Players[i] == null )
					return i;
			}

			for ( int i = 0; i < m_DealerPosition; i++ )
			{
				if ( m_Players[i] == null )
					return i;
			}

			return -1;
		}

		private bool UpdateSpectators()
		{
			if ( m_Spectators.Count == 0 )
				return false;

			int nextSeat = GetNextSeat();

			if ( nextSeat >= 0 )
			{
				Mobile newPlayer = (Mobile)m_Spectators[0];

				m_Spectators.RemoveAt( 0 );

				AddPlayer( newPlayer, nextSeat, false );

				UpdateSpectators();

				return true;
			}
			else
			{
				return false;
			}
		}

		private void AddPlayer( Mobile player, int index, bool sendJoinGame )
		{
			m_Players[index] = player;
			m_InGame[index] = true;

			UpdateDealer( false );

			if ( sendJoinGame )
				player.Send( new MahjongJoinGame( m_Game ) );

			SendPlayersPacket( true, true );

			player.Send( new MahjongGeneralInfo( m_Game ) );
			player.Send( new MahjongTilesInfo( m_Game, player ) );

			if ( m_DealerPosition == index )
				SendLocalizedMessage( 1062773, player.Name ); // ~1_name~ has entered the game as the dealer.
			else
				SendLocalizedMessage( 1062772, player.Name ); // ~1_name~ has entered the game as a player.
		}

		private void AddSpectator( Mobile mobile )
		{
			if ( !IsSpectator( mobile ) )
			{
				m_Spectators.Add( mobile );
			}

			mobile.Send( new MahjongJoinGame( m_Game ) );
			mobile.Send( new MahjongPlayersInfo( m_Game, mobile ) );
			mobile.Send( new MahjongGeneralInfo( m_Game ) );
			mobile.Send( new MahjongTilesInfo( m_Game, mobile ) );
		}

		public void Join( Mobile mobile )
		{
			int index = GetPlayerIndex( mobile );

			if ( index >= 0 )
			{
				AddPlayer( mobile, index, true );
			}
			else
			{
				int nextSeat = GetNextSeat();

				if ( nextSeat >= 0 )
				{
					AddPlayer( mobile, nextSeat, true );
				}
				else
				{
					AddSpectator( mobile );
				}
			}
		}

		public void LeaveGame( Mobile player )
		{
			int index = GetPlayerIndex( player );
			if ( index >= 0 )
			{
				m_InGame[index] = false;

				SendPlayerExitMessage( player );
				UpdateDealer( true );

				SendPlayersPacket( true, true );
			}
			else
			{
				m_Spectators.Remove( player );
			}
		}

		public void ResetScores( int value )
		{
			for ( int i = 0; i < m_Scores.Length; i++ )
			{
				m_Scores[i] = value;
			}

			SendPlayersPacket( true, m_Game.ShowScores );

			SendLocalizedMessage( 1062697 ); // The dealer redistributes the score sticks evenly.
		}

		public void TransferScore( Mobile from, int toPosition, int amount )
		{
			int fromPosition = GetPlayerIndex( from );
			Mobile to = GetPlayer( toPosition );

			if ( fromPosition < 0 || to == null || m_Scores[fromPosition] < amount )
				return;

			m_Scores[fromPosition] -= amount;
			m_Scores[toPosition] += amount;

			if ( m_Game.ShowScores )
			{
				SendPlayersPacket( true, true );
			}
			else
			{
				from.Send( new MahjongPlayersInfo( m_Game, from ) );
				to.Send( new MahjongPlayersInfo( m_Game, to ) );
			}

			SendLocalizedMessage( 1062774, string.Format( "{0}\t{1}\t{2}", from.Name, to.Name, amount ) ); // ~1_giver~ gives ~2_reciever~ ~3_number~ points.
		}

		public void OpenSeat( int index )
		{
			Mobile player = GetPlayer( index );
			if ( player == null )
				return;

			if ( m_InGame[index] )
				player.Send( new MahjongRelieve( m_Game ) );

			m_Players[index] = null;

			SendLocalizedMessage( 1062699, player.Name ); // ~1_name~ is relieved from the game by the dealer.

			UpdateDealer( true );

			if ( !UpdateSpectators() )
				SendPlayersPacket( true, true );
		}

		public void AssignDealer( int index )
		{
			Mobile to = GetPlayer( index );

			if ( to == null || !m_InGame[index] )
				return;

			int oldDealer = m_DealerPosition;

			m_DealerPosition = index;

			if ( IsInGamePlayer( oldDealer ) )
				m_Players[oldDealer].Send( new MahjongPlayersInfo( m_Game, m_Players[oldDealer] ) );

			to.Send( new MahjongPlayersInfo( m_Game, to ) );

			SendDealerChangedMessage();
		}

		private void SendDealerChangedMessage()
		{
			if ( Dealer != null )
				SendLocalizedMessage( 1062698, Dealer.Name ); // ~1_name~ is assigned the dealer.
		}

		private void SendPlayerExitMessage( Mobile who )
		{
			SendLocalizedMessage( 1062762, who.Name ); // ~1_name~ has left the game.
		}

		public void SendPlayersPacket( bool players, bool spectators )
		{
			foreach ( Mobile mobile in GetInGameMobiles( players, spectators ) )
			{
				mobile.Send( new MahjongPlayersInfo( m_Game, mobile ) );
			}
		}

		public void SendGeneralPacket( bool players, bool spectators )
		{
			ArrayList mobiles = GetInGameMobiles( players, spectators );

			if ( mobiles.Count == 0 )
				return;

			MahjongGeneralInfo generalInfo = new MahjongGeneralInfo( m_Game );

			generalInfo.Acquire();

			foreach ( Mobile mobile in mobiles )
			{
				mobile.Send( generalInfo );
			}

			generalInfo.Release();
		}

		public void SendTilesPacket( bool players, bool spectators )
		{
			foreach ( Mobile mobile in GetInGameMobiles( players, spectators ) )
			{
				mobile.Send( new MahjongTilesInfo( m_Game, mobile ) );
			}
		}

		public void SendTilePacket( MahjongTile tile, bool players, bool spectators )
		{
			foreach ( Mobile mobile in GetInGameMobiles( players, spectators ) )
			{
				mobile.Send( new MahjongTileInfo( tile, mobile ) );
			}
		}

		public void SendRelievePacket( bool players, bool spectators )
		{
			ArrayList mobiles = GetInGameMobiles( players, spectators );

			if ( mobiles.Count == 0 )
				return;

			MahjongRelieve relieve = new MahjongRelieve( m_Game );

			relieve.Acquire();

			foreach ( Mobile mobile in mobiles )
			{
				mobile.Send( relieve );
			}

			relieve.Release();
		}

		public void SendLocalizedMessage( int number )
		{
			foreach ( Mobile mobile in GetInGameMobiles( true, true ) )
			{
				mobile.SendLocalizedMessage( number );
			}
		}

		public void SendLocalizedMessage( int number, string args )
		{
			foreach ( Mobile mobile in GetInGameMobiles( true, true ) )
			{
				mobile.SendLocalizedMessage( number, args );
			}
		}

		public void Save( GenericWriter writer )
		{
			writer.Write( (int) 0 ); // version

			writer.Write( Seats );

			for ( int i = 0; i < Seats; i++ )
			{
				writer.Write( m_Players[i] );
				writer.Write( m_PublicHand[i] );
				writer.Write( m_Scores[i] );
			}

			writer.Write( m_DealerPosition );
		}

		public MahjongPlayers( MahjongGame game, GenericReader reader )
		{
			m_Game = game;
			m_Spectators = new ArrayList();

			int version = reader.ReadInt();

			int seats = reader.ReadInt();
			m_Players = new Mobile[seats];
			m_InGame = new bool[seats];
			m_PublicHand = new bool[seats];
			m_Scores = new int[seats];

			for ( int i = 0; i < seats; i++ )
			{
				m_Players[i] = reader.ReadMobile();
				m_PublicHand[i] = reader.ReadBool();
				m_Scores[i] = reader.ReadInt();
			}

			m_DealerPosition = reader.ReadInt();
		}
	}
}