using System;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Accounting;

namespace Server.Misc
{
	public class CharacterCreation
	{
		public static void Initialize()
		{
			// Register our event handler
			EventSink.CharacterCreated += new CharacterCreatedEventHandler( EventSink_CharacterCreated );
		}

		private static void AddBackpack( Mobile m )
		{
			Container pack = m.Backpack;

			if ( pack == null )
			{
				pack = new Backpack();
				pack.Movable = false;

				m.AddItem( pack );
			}

			PackItem( new RedBook( "a book", m.Name, 20, true ) );
			PackItem( new Gold( Server.Misc.Settings.StartGold() ) );
			PackItem( new Dagger() );
			PackItem( new Candle() );
		}

		private static void AddShirt( Mobile m )
		{
			switch ( Utility.Random( 5 ) )
			{
				case 0: EquipItem( new FancyShirt( Utility.RandomHue() ), true ); break;
				case 1: EquipItem( new Doublet( Utility.RandomHue() ), true ); break;
				case 2: EquipItem( new Shirt( Utility.RandomHue() ), true ); break;
				case 3: EquipItem( new Tunic( Utility.RandomHue() ), true ); break;
				case 4: EquipItem( new Surcoat( Utility.RandomHue() ), true ); break;
			}
		}

		private static void AddPants( Mobile m )
		{
			if ( m.Female )
			{
				switch ( Utility.Random( 4 ) )
				{
					case 0: EquipItem( new Skirt( Utility.RandomHue() ), true ); break;
					case 1: EquipItem( new Kilt( Utility.RandomHue() ), true ); break;
					case 2: EquipItem( new LongPants( Utility.RandomHue() ), true ); break;
					case 3: EquipItem( new ShortPants( Utility.RandomHue() ), true ); break;
				}
			}
			else
			{
				switch ( Utility.Random( 3 ) )
				{
					case 0: EquipItem( new LongPants( Utility.RandomHue() ), true ); break;
					case 1: EquipItem( new ShortPants( Utility.RandomHue() ), true ); break;
					case 2: EquipItem( new Kilt( Utility.RandomHue() ), true ); break;
				}
			}
		}

		private static void AddShoes( Mobile m )
		{
			switch ( Utility.Random( 4 ) )
			{
				case 0: EquipItem( new Shoes( Utility.RandomNeutralHue() ), true ); break;
				case 1: EquipItem( new Boots( Utility.RandomNeutralHue() ), true );	break;
				case 2: EquipItem( new Sandals( Utility.RandomNeutralHue() ), true );	break;
				case 3: EquipItem( new ThighBoots( Utility.RandomNeutralHue() ), true );	break;
			}
		}

		private static void AddHat( Mobile m, int color )
		{
			switch ( Utility.Random( 7 ) )
			{
				case 0: EquipItem( new FloppyHat( Utility.RandomHue() ), true ); break;
				case 1: EquipItem( new Hood( color ), true ); break;
				case 2: EquipItem( new WideBrimHat( Utility.RandomHue() ), true ); break;
				case 3: EquipItem( new Cap( Utility.RandomHue() ), true ); break;
				case 4: EquipItem( new SkullCap( Utility.RandomHue() ), true ); break;
				case 5: EquipItem( new Bandana( Utility.RandomHue() ), true ); break;
				case 6: EquipItem( new FeatheredHat( Utility.RandomHue() ), true ); break;
			}
		}

		private static Mobile CreateMobile( Account a )
		{
			if ( a.Count >= a.Limit )
				return null;

			for ( int i = 0; i < a.Length; ++i )
			{
				if ( a[i] == null )
					return (a[i] = new PlayerMobile());
			}

			return null;
		}

		private static void EventSink_CharacterCreated( CharacterCreatedEventArgs args )
		{
			if ( !VerifyProfession( args.Profession ) )
				args.Profession = 0;

			NetState state = args.State;

			if ( state == null )
				return;

			Mobile newChar = CreateMobile( args.Account as Account );

			if ( newChar == null )
			{
				Console.WriteLine( "Login: {0}: Character creation failed, account full", state );
				return;
			}

			args.Mobile = newChar;
			m_Mobile = newChar;

			newChar.Player = true;
			newChar.StatCap = 200; 
			newChar.Skills.Cap = 7000;
			newChar.AccessLevel = args.Account.AccessLevel;
			newChar.Female = args.Female;
			newChar.Race = Race.Human;
			newChar.Hue = newChar.Race.ClipSkinHue( args.Hue & 0x3FFF ) | 0x8000;
			newChar.Hunger = 20;
			newChar.Thirst = 20;

			if ( newChar is PlayerMobile )
			{
				PlayerMobile pm = (PlayerMobile) newChar;

				pm.Profession = args.Profession;
			}

			SetName( newChar, args.Name );

			AddBackpack( newChar );

			SetStats( newChar, state, args.Str, args.Dex, args.Int, args.Profession );
			SetSkills( newChar, args.Skills, args.Profession );

			newChar.HairItemID = args.HairID;
			newChar.HairHue = ClipHairHue( args.HairHue & 0x3FFF );
			newChar.FacialHairItemID = args.BeardID;
			newChar.FacialHairHue = ClipHairHue( args.BeardHue & 0x3FFF );

			newChar.RecordHair = newChar.HairItemID;;
			newChar.RecordBeard = newChar.FacialHairItemID;;

			AddShirt( newChar );
			AddPants( newChar );
			AddShoes( newChar );

			int hatColor = 0;
			int robeColor = 0;

			if ( Utility.RandomBool() )
			{
				hatColor = Utility.RandomHue();
				EquipItem( new Cloak( hatColor ), true );
			}
			if ( Utility.RandomBool() )
			{ 
				robeColor = Utility.RandomHue();	
				EquipItem( new Robe( robeColor ), true ); 
				if ( hatColor > 0 && Utility.RandomBool() ){ hatColor = robeColor; } 
				else if ( hatColor == 0 ){ hatColor = robeColor; }
			}
			if ( Utility.RandomBool() ){ AddHat( newChar, hatColor ); }

			CityInfo city = GetStartLocation( args );

			newChar.MoveToWorld( city.Location, city.Map );

			Console.WriteLine( "Login: {0}: New character being created (account={1})", state, args.Account.Username );
			Console.WriteLine( " - Character: {0} (serial={1})", newChar.Name, newChar.Serial );
			Console.WriteLine( " - Started: {0} {1} in {2}", city.City, city.Location, city.Map.ToString() );

			new WelcomeTimer( newChar ).Start();
		}

		public static int ClipHairHue( int hue )
		{
			if( hue < 1102 )
				return 1102;
			else if( hue > 1149 )
				return 1149;
			else
				return hue;
		}

		public static bool VerifyProfession( int profession )
		{
			if ( profession < 8 )
				return true;
			else
				return false;
		}

		private class BadStartMessage : Timer
		{
			Mobile m_Mobile;
			int m_Message;
			public BadStartMessage( Mobile m, int message ) : base( TimeSpan.FromSeconds ( 3.5 ) )
			{
				m_Mobile = m;
				m_Message = message;
				this.Start();
			}

			protected override void OnTick()
			{
				m_Mobile.SendLocalizedMessage( m_Message );
			}
		}

		private static CityInfo GetStartLocation( CharacterCreatedEventArgs args )
		{
			return args.City;
		}

		private static void FixStats( ref int str, ref int dex, ref int intel, int max )
		{
			int vMax = max - 30;

			int vStr = str - 10;
			int vDex = dex - 10;
			int vInt = intel - 10;

			if ( vStr < 0 )
				vStr = 0;

			if ( vDex < 0 )
				vDex = 0;

			if ( vInt < 0 )
				vInt = 0;

			int total = vStr + vDex + vInt;

			if ( total == 0 || total == vMax )
				return;

			double scalar = vMax / (double)total;

			vStr = (int)(vStr * scalar);
			vDex = (int)(vDex * scalar);
			vInt = (int)(vInt * scalar);

			FixStat( ref vStr, (vStr + vDex + vInt) - vMax, vMax );
			FixStat( ref vDex, (vStr + vDex + vInt) - vMax, vMax );
			FixStat( ref vInt, (vStr + vDex + vInt) - vMax, vMax );

			str = vStr + 10;
			dex = vDex + 10;
			intel = vInt + 10;
		}

		private static void FixStat( ref int stat, int diff, int max )
		{
			stat += diff;

			if ( stat < 0 )
				stat = 0;
			else if ( stat > max )
				stat = max;
		}

		private static void SetStats( Mobile m, NetState state, int str, int dex, int intel, int prof )
		{
			int max = state.NewCharacterCreation ? 90 : 80;

			FixStats( ref str, ref dex, ref intel, max );

			if ( str < 10 || str > 60 || dex < 10 || dex > 60 || intel < 10 || intel > 60 || (str + dex + intel) != max )
			{
				str = 10;
				dex = 10;
				intel = 10;
			}

			if ( prof > 0 )
			{
				switch ( prof )
				{
					case 1: // Swordsman
					{
						m.InitStats( 45, 25, 10 );		break;
					}
					case 2: // Magician
					{
						m.InitStats( 15, 20, 45 );		break;
					}
					case 3: // Thief
					{
						m.InitStats( 20, 45, 15 );		break;
					}
					case 4: // Fencer
					{
						m.InitStats( 30, 40, 10 );		break;
					}
					case 5: // Mace Fighter
					{
						m.InitStats( 45, 25, 10 );		break;
					}
					case 6:	// Archer
					{
						m.InitStats( 25, 45, 10 );		break;
					}
					case 7:	// Bard
					{
						m.InitStats( 25, 35, 20 );		break;
					}
				}
			}
			else
				m.InitStats( str, dex, intel );
		}

		private static void SetName( Mobile m, string name )
		{
			name = name.Trim();

			if ( !NameVerification.Validate( name, 2, 16, true, false, true, 1, NameVerification.SpaceDashPeriodQuote ) )
				name = "Generic Player";

			m.Name = name;
		}

		private static bool ValidSkills( SkillNameValue[] skills )
		{
			int total = 0;

			for ( int i = 0; i < skills.Length; ++i )
			{
				if ( skills[i].Value < 0 || skills[i].Value > 50 )
					return false;

				total += skills[i].Value;

				for ( int j = i + 1; j < skills.Length; ++j )
				{
					if ( skills[j].Value > 0 && skills[j].Name == skills[i].Name )
						return false;
				}
			}

			return ( total == 100 || total == 120 );
		}

		private static Mobile m_Mobile;

		private static void SetSkills( Mobile m, SkillNameValue[] skills, int prof )
		{
			switch ( prof )
			{
				case 1: // Swordsman
				{
					skills = new SkillNameValue[]
						{
							new SkillNameValue( SkillName.Healing, 30 ),
							new SkillNameValue( SkillName.Swords, 40 ),
							new SkillNameValue( SkillName.Tactics, 30 )
						};

					break;
				}
				case 2: // Magician
				{
					skills = new SkillNameValue[]
						{
							new SkillNameValue( SkillName.Magery, 50 ),
							new SkillNameValue( SkillName.Meditation, 50 )
						};

					break;
				}
				case 3: // Thief
				{
					skills = new SkillNameValue[]
						{
							new SkillNameValue( SkillName.Hiding, 50 ),
							new SkillNameValue( SkillName.Stealing, 50 )
						};

					break;
				}
				case 4: // Fencer
				{
					skills = new SkillNameValue[]
						{
							new SkillNameValue( SkillName.Healing, 30 ),
							new SkillNameValue( SkillName.Fencing, 40 ),
							new SkillNameValue( SkillName.Tactics, 30 )
						};

					break;
				}
				case 5: // Mace Fighter
				{
					skills = new SkillNameValue[]
						{
							new SkillNameValue( SkillName.Healing, 30 ),
							new SkillNameValue( SkillName.Bludgeoning, 40 ),
							new SkillNameValue( SkillName.Tactics, 30 )
						};

					break;
				}
				case 6:	// Archer
				{
					skills = new SkillNameValue[]
						{
							new SkillNameValue( SkillName.Archery, 50 ),
							new SkillNameValue( SkillName.Tactics, 50 )
					};
					break;
				}
				case 7:	// Bard
				{
					skills = new SkillNameValue[]
						{
							new SkillNameValue( SkillName.Musicianship, 40 ),
							new SkillNameValue( SkillName.Discordance, 30 ),
							new SkillNameValue( SkillName.Peacemaking, 30 )
						};
					break;
				}
				default:
				{
					if ( !ValidSkills( skills ) )
						return;

					break;
				}
			}

			for ( int i = 0; i < skills.Length; ++i )
			{
				SkillNameValue snv = skills[i];

				if ( snv.Value > 0 )
				{
					Skill skill = m.Skills[snv.Name];

					if ( skill != null )
					{
						skill.BaseFixedPoint = snv.Value * 10;
					}
				}
			}
		}

		private static void EquipItem( Item item )
		{
			EquipItem( item, false );
		}

		private static void EquipItem( Item item, bool mustEquip )
		{
			if ( m_Mobile != null && m_Mobile.EquipItem( item ) )
				return;

			Container pack = m_Mobile.Backpack;

			if ( !mustEquip && pack != null )
				pack.DropItem( item );
			else
				item.Delete();
		}

		private static void PackItem( Item item )
		{
			Container pack = m_Mobile.Backpack;

			if ( pack != null )
				pack.DropItem( item );
			else
				item.Delete();
		}
	}
}