using System;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Engines.Quests;
using Server.Engines.Quests.Collector;

namespace Server.Engines.Harvest
{
	public class Fishing : HarvestSystem
	{
		private static Fishing m_System;

		public static Fishing System
		{
			get
			{
				if ( m_System == null )
					m_System = new Fishing();

				return m_System;
			}
		}

		private HarvestDefinition m_Definition;

		public HarvestDefinition Definition
		{
			get{ return m_Definition; }
		}

		private Fishing()
		{
			HarvestResource[] res;
			HarvestVein[] veins;

			#region Fishing
			HarvestDefinition fish = new HarvestDefinition();

			// Resource banks are every 8x8 tiles
			fish.BankWidth = 8;
			fish.BankHeight = 8;

			// Every bank holds from 5 to 15 fish
			fish.MinTotal = 5;
			fish.MaxTotal = 15;

			// A resource bank will respawn its content every 10 to 20 minutes
			fish.MinRespawn = TimeSpan.FromMinutes( 10.0 );
			fish.MaxRespawn = TimeSpan.FromMinutes( 20.0 );

			// Skill checking is done on the Fishing skill
			fish.Skill = SkillName.Fishing;

			// Set the list of harvestable tiles
			fish.Tiles = m_WaterTiles;
			fish.RangedTiles = true;

			// Players must be within 4 tiles to harvest
			fish.MaxRange = 4;

			// One fish per harvest action
			fish.ConsumedPerHarvest = 1;
			fish.ConsumedPerFeluccaHarvest = 1;

			// The fishing
			fish.EffectActions = new int[]{ 12 };
			fish.EffectSounds = new int[0];
			fish.EffectCounts = new int[]{ 1 };
			fish.EffectDelay = TimeSpan.Zero;
			fish.EffectSoundDelay = TimeSpan.FromSeconds( 8.0 );

			fish.NoResourcesMessage = 503172; // The fish don't seem to be biting here.
			fish.FailMessage = 503171; // You fish a while, but fail to catch anything.
			fish.TimedOutOfRangeMessage = 500976; // You need to be closer to the water to fish!
			fish.OutOfRangeMessage = 500976; // You need to be closer to the water to fish!
			fish.PackFullMessage = 503176; // You do not have room in your backpack for a fish.
			fish.ToolBrokeMessage = 503174; // You broke your fishing pole.

			res = new HarvestResource[]
				{
					new HarvestResource( 00.0, 00.0, 100.0, 1043297, typeof( Fish ) )
				};

			veins = new HarvestVein[]
				{
					new HarvestVein( 100.0, 0.0, res[0], null )
				};

			fish.Resources = res;
			fish.Veins = veins;

			m_Definition = fish;
			Definitions.Add( fish );
			#endregion
		}

		public override void OnConcurrentHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest )
		{
			from.SendLocalizedMessage( 500972 ); // You are already fishing.
		}

		private class MutateEntry
		{
			public double m_ReqSkill, m_MinSkill, m_MaxSkill;
			public bool m_DeepWater;
			public Type[] m_Types;

			public MutateEntry( double reqSkill, double minSkill, double maxSkill, bool deepWater, params Type[] types )
			{
				m_ReqSkill = reqSkill;
				m_MinSkill = minSkill;
				m_MaxSkill = maxSkill;
				m_DeepWater = deepWater;
				m_Types = types;
			}
		}

		private static MutateEntry[] m_MutateTable = new MutateEntry[]
			{
				new MutateEntry(  80.0,  80.0,  4080.0,  true, typeof( SpecialFishingNet ) ),
				new MutateEntry(  80.0,  80.0,  4080.0,  true, typeof( BigFish ) ),
				new MutateEntry(  90.0,  80.0,  4080.0,  true, typeof( TreasureMap ) ),
				new MutateEntry( 100.0,  80.0,  4080.0,  true, typeof( MessageInABottle ) ),
				new MutateEntry(   0.0, 125.0, -2375.0, false, typeof( PrizedFish ), typeof( WondrousFish ), typeof( TrulyRareFish ), typeof( PeculiarFish ) ),
				new MutateEntry(   0.0, 105.0,  -420.0, false, typeof( Boots ), typeof( Shoes ), typeof( Sandals ), typeof( ThighBoots ) ),
				new MutateEntry(   0.0, 200.0,  -200.0, false, new Type[1]{ null } )
			};

		public override bool SpecialHarvest( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc )
		{
			PlayerMobile player = from as PlayerMobile;

			if ( player != null )
			{
				QuestSystem qs = player.Quest;

				if ( qs is CollectorQuest )
				{
					QuestObjective obj = qs.FindObjective( typeof( FishPearlsObjective ) );

					if ( obj != null && !obj.Completed )
					{
						if ( Utility.RandomDouble() < 0.5 )
						{
							player.SendLocalizedMessage( 1055086, "", 0x59 ); // You pull a shellfish out of the water, and find a rainbow pearl inside of it.

							obj.CurProgress++;
						}
						else
						{
							player.SendLocalizedMessage( 1055087, "", 0x2C ); // You pull a shellfish out of the water, but it doesn't have a rainbow pearl.
						}

						return true;
					}
				}
			}

			return false;
		}

		public override Type MutateType( Type type, Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource )
		{
			bool deepWater = SpecialFishingNet.FullValidation( map, loc.X, loc.Y );

			double skillBase = from.Skills[SkillName.Fishing].Base;
			double skillValue = from.Skills[SkillName.Fishing].Value;

			for ( int i = 0; i < m_MutateTable.Length; ++i )
			{
				MutateEntry entry = m_MutateTable[i];

				if ( !deepWater && entry.m_DeepWater )
					continue;

				if ( skillBase >= entry.m_ReqSkill )
				{
					double chance = (skillValue - entry.m_MinSkill) / (entry.m_MaxSkill - entry.m_MinSkill);

					if ( chance > Utility.RandomDouble() )
						return entry.m_Types[Utility.Random( entry.m_Types.Length )];
				}
			}

			return type;
		}

		private static Map SafeMap( Map map )
		{
			if ( map == null || map == Map.Internal )
				return Map.Felucca;

			return map;
		}

		public override bool CheckResources( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed )
		{
			Container pack = from.Backpack;

			if ( pack != null )
			{
				Item[] messages = pack.FindItemsByType( typeof( SOS ) );

				for ( int i = 0; i < messages.Length; ++i )
				{
					SOS sos = (SOS)messages[i];

					if ( from.Map == sos.TargetMap && from.InRange( sos.TargetLocation, 60 ) )
						return true;
				}
			}

			return base.CheckResources( from, tool, def, map, loc, timed );
		}

		public override Item Construct( Type type, Mobile from )
		{
			if ( type == typeof( TreasureMap ) )
			{
				int level;
				if ( from is PlayerMobile && ((PlayerMobile)from).Young && from.Map == Map.Trammel && TreasureMap.IsInHavenIsland( from ) )
					level = 0;
				else
					level = 1;

				return new TreasureMap( level, from.Map == Map.Felucca ? Map.Felucca : Map.Trammel );
			}
			else if ( type == typeof( MessageInABottle ) )
			{
				return new MessageInABottle( SafeMap( from.Map ) );
			}

			Container pack = from.Backpack;

			if ( pack != null )
			{
				Item[] messages = pack.FindItemsByType( typeof( SOS ) );

				for ( int i = 0; i < messages.Length; ++i )
				{
					SOS sos = (SOS)messages[i];

					if ( from.Map == sos.TargetMap && from.InRange( sos.TargetLocation, 60 ) )
					{
						Item preLoot = null;

						switch ( Utility.Random( 7 ) )
						{
							case 0: // Body parts
							{
								int[] list = new int[]
									{
										0x1CDD, 0x1CE5, // arm
										0x1CE0, 0x1CE8, // torso
										0x1CE1, 0x1CE9, // head
										0x1CE2, 0x1CEC // leg
									};

								preLoot = new ShipwreckedItem( Utility.RandomList( list ) );
								break;
							}
							case 1: // Bone parts
							{
								int[] list = new int[]
									{
										0x1AE0, 0x1AE1, 0x1AE2, 0x1AE3, 0x1AE4, // skulls
										0x1B09, 0x1B0A, 0x1B0B, 0x1B0C, 0x1B0D, 0x1B0E, 0x1B0F, 0x1B10, // bone piles
										0x1B15, 0x1B16 // pelvis bones
									};

								preLoot = new ShipwreckedItem( Utility.RandomList( list ) );
								break;
							}
							case 2: // Paintings and portraits
							{
								preLoot = new ShipwreckedItem( Utility.Random( 0xE9F, 10 ) );
								break;
							}
							case 3: // Pillows
							{
								preLoot = new ShipwreckedItem( Utility.Random( 0x13A4, 11 ) );
								break;
							}
							case 4: // Shells
							{
								preLoot = new ShipwreckedItem( Utility.Random( 0xFC4, 9 ) );
								break;
							}
							case 5: // Misc
							{
								int[] list = new int[]
									{
										0x1EB5, // unfinished barrel
										0xA2A, // stool
										0xC1F, // broken clock
										0x1047, 0x1048, // globe
										0x1EB1, 0x1EB2, 0x1EB3, 0x1EB4 // barrel staves
									};

								preLoot = new ShipwreckedItem( Utility.RandomList( list ) );
								break;
							}
						}

						if ( preLoot != null )
							return preLoot;

						LockableContainer chest;
						
						if ( Utility.RandomBool() )
							chest = new MetalGoldenChest();
						else
							chest = new WoodenChest();

						if ( sos.IsAncient )
							chest.Hue = 0x481;

						TreasureMapChest.Fill( chest, Math.Max( 1, Math.Max( 4, sos.Level ) ) );

						if ( sos.IsAncient )
							chest.DropItem( new FabledFishingNet() );
						else
							chest.DropItem( new SpecialFishingNet() );

						chest.Movable = true;
						chest.Locked = false;
						chest.TrapType = TrapType.None;
						chest.TrapPower = 0;
						chest.TrapLevel = 0;

						sos.Delete();

						return chest;
					}
				}
			}

			return base.Construct( type, from );
		}

		public override bool Give( Mobile m, Item item, bool placeAtFeet )
		{
			if ( item is TreasureMap || item is MessageInABottle || item is SpecialFishingNet )
			{
				BaseCreature serp;

				if ( 0.25 > Utility.RandomDouble() )
					serp = new DeepSeaSerpent();
				else
					serp = new SeaSerpent();

				int x = m.X, y = m.Y;

				Map map = m.Map;

				for ( int i = 0; map != null && i < 20; ++i )
				{
					int tx = m.X - 10 + Utility.Random( 21 );
					int ty = m.Y - 10 + Utility.Random( 21 );

					Tile t = map.Tiles.GetLandTile( tx, ty );

					if ( t.Z == -5 && ( (t.ID >= 0xA8 && t.ID <= 0xAB) || (t.ID >= 0x136 && t.ID <= 0x137) ) && !Spells.SpellHelper.CheckMulti( new Point3D( tx, ty, -5 ), map ) )
					{
						x = tx;
						y = ty;
						break;
					}
				}

				serp.MoveToWorld( new Point3D( x, y, -5 ), map );

				serp.Home = serp.Location;
				serp.RangeHome = 10;

				serp.PackItem( item );

				m.SendLocalizedMessage( 503170 ); // Uh oh! That doesn't look like a fish!

				return true; // we don't want to give the item to the player, it's on the serpent
			}

			if ( item is BigFish || item is WoodenChest || item is MetalGoldenChest )
				placeAtFeet = true;

			return base.Give( m, item, placeAtFeet );
		}

		public override void SendSuccessTo( Mobile from, Item item, HarvestResource resource )
		{
			if ( item is BigFish )
			{
				from.SendLocalizedMessage( 1042635 ); // Your fishing pole bends as you pull a big fish from the depths!

				((BigFish)item).Fisher = from;
			}
			else if ( item is WoodenChest || item is MetalGoldenChest )
			{
				from.SendLocalizedMessage( 503175 ); // You pull up a heavy chest from the depths of the ocean!
			}
			else
			{
				int number;
				string name;

				if ( item is BaseMagicFish )
				{
					number = 1008124;
					name = "a mess of small fish";
				}
				else if ( item is Fish )
				{
					number = 1008124;
					name = "a fish";
				}
				else if ( item is BaseShoes )
				{
					number = 1008124;
					name = item.ItemData.Name;
				}
				else if ( item is TreasureMap )
				{
					number = 1008125;
					name = "a sodden piece of parchment";
				}
				else if ( item is MessageInABottle )
				{
					number = 1008125;
					name = "a bottle, with a message in it";
				}
				else if ( item is SpecialFishingNet )
				{
					number = 1008125;
					name = "a special fishing net"; // TODO: this is just a guess--what should it really be named?
				}
				else
				{
					number = 1043297;

					if ( (item.ItemData.Flags & TileFlag.ArticleA) != 0 )
						name = "a " + item.ItemData.Name;
					else if ( (item.ItemData.Flags & TileFlag.ArticleAn) != 0 )
						name = "an " + item.ItemData.Name;
					else
						name = item.ItemData.Name;
				}

				if ( number == 1043297 )
					from.SendLocalizedMessage( number, name );
				else
					from.SendLocalizedMessage( number, true, name );
			}
		}

		public override void OnHarvestStarted( Mobile from, Item tool, HarvestDefinition def, object toHarvest )
		{
			base.OnHarvestStarted( from, tool, def, toHarvest );

			int tileID;
			Map map;
			Point3D loc;

			if ( GetHarvestDetails( from, tool, toHarvest, out tileID, out map, out loc ) )
				Timer.DelayCall( TimeSpan.FromSeconds( 1.5 ), new TimerStateCallback( Splash_Callback ), new object[]{ loc, map } );
		}

		private void Splash_Callback( object state )
		{
			object[] args = (object[])state;
			Point3D loc = (Point3D)args[0];
			Map map = (Map)args[1];

			Effects.SendLocationEffect( loc, map, 0x352D, 16, 4 );
			Effects.PlaySound( loc, map, 0x364 );
		}

		public override object GetLock( Mobile from, Item tool, HarvestDefinition def, object toHarvest )
		{
			return this;
		}

		public override bool BeginHarvesting( Mobile from, Item tool )
		{
			if ( !base.BeginHarvesting( from, tool ) )
				return false;

			from.SendLocalizedMessage( 500974 ); // What water do you want to fish in?
			return true;
		}

		public override bool CheckHarvest( Mobile from, Item tool )
		{
			if ( !base.CheckHarvest( from, tool ) )
				return false;

			if ( from.Mounted )
			{
				from.SendLocalizedMessage( 500971 ); // You can't fish while riding!
				return false;
			}

			return true;
		}

		public override bool CheckHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest )
		{
			if ( !base.CheckHarvest( from, tool, def, toHarvest ) )
				return false;

			if ( from.Mounted )
			{
				from.SendLocalizedMessage( 500971 ); // You can't fish while riding!
				return false;
			}

			return true;
		}

		private static int[] m_WaterTiles = new int[]
			{
				0x00A8, 0x00AB,
				0x0136, 0x0137,
				0x5797, 0x579C,
				0x746E, 0x7485,
				0x7490, 0x74AB,
				0x74B5, 0x75D5
			};
	}
}