using System;
using System.Collections.Generic;
using System.Linq;

using Server;
using Server.Mobiles;
using Server.ContextMenus;
using Server.Engines.Points;
using Server.Engines;
using Server.Items;
using Server.Multis;
using Server.Regions;
using Server.Engines.SeasonalEvents;

namespace Server.Engines.CityLoyalty
{
    public enum TradeTitle
    {
        Trader      = 1151739,
        Exporter    = 1151741,
        Broker      = 1151743,
        Tycoon      = 1151745,
        Smuggler    = 1151747,
        Magnate     = 1155481
    }

	public class CityTradeSystem : PointsSystem
	{
        public static readonly int TurnInGold = Config.Get("CityTrading.TurnInGold", 10000);
        public static readonly int CrateDuration = Config.Get("CityTrading.CrateDuration", 24);
        public static readonly int AmbushWaitDuration = Config.Get("CityTrading.AmbushWaitDuration", 5);
        public static readonly int AmbusherDelete = Config.Get("CityTrading.AmbusherDelete", 10);

        public override TextDefinition Name { get { return new TextDefinition("City Trading"); } }
        public override PointsType Loyalty { get { return PointsType.CityTrading; } }
        public override bool AutoAdd { get { return false; } }
        public override double MaxPoints { get { return double.MaxValue; } }
        public override bool ShowOnLoyaltyGump { get { return false; } }

        public static bool KrampusEncounterActive { get { return KrampusEncounter.Enabled && KrampusEncounter.Encounter != null; } }

        public static Dictionary<Mobile, TradeOrderCrate> ActiveTrades { get; private set; }
        public static Dictionary<BaseCreature, DateTime> Ambushers { get; private set; }
		
		public CityTradeSystem() 
		{
			ActiveTrades = new Dictionary<Mobile, TradeOrderCrate>();

            _NameBuffer = new Dictionary<Type, string>();
		}

        public override PointsEntry GetSystemEntry(PlayerMobile pm)
        {
            return new CityTradeEntry(pm);
        }

		public int GetMaxTrades(Mobile m)
		{
            CityTradeEntry entry = GetPlayerEntry<CityTradeEntry>(m as PlayerMobile);

			if(entry == null)
				return 1;

            return Math.Min(8, Math.Max(1, (entry.Completed / 25) + 1));
		}
		
		public static bool HasTrade(Mobile from)
		{
            return ActiveTrades.ContainsKey(from);
		}
		
		public bool HasTurnIn(Mobile from, TradeMinister minister)
		{
			if(from == null || minister == null || !ActiveTrades.ContainsKey(from) || ActiveTrades[from] == null)
				return false;

            TradeOrderCrate crate = ActiveTrades[from];

            return crate.Entry != null && crate.Entry.Origin != minister.City;
		}
		
		public bool TryOfferTrade(Mobile from, TradeMinister minister)
		{
			if(from == null || from.Backpack == null)
				return true;

            if (ActiveTrades.ContainsKey(from))
            {
                minister.SayTo(from, 1151722); // It appears you are already delivering a trade order. Deliver your current order before requesting another.
            }
            else if (KrampusEncounterActive && (KrampusEncounter.Encounter.Krampus != null || KrampusEncounter.Encounter.KrampusSpawning))
            {
                var p = KrampusEncounter.Encounter.SpawnLocation;
                var map = KrampusEncounter.Encounter.SpawnMap;

                minister.SayTo(
                    from,
                    1158790,
                    String.Format("{0}\t{1}",
                    WorldLocationInfo.GetLocationString(p, map),
                    Sextant.GetCoords(p, map)), 1150);
                    // Take notice! The vile Krampus has been spotted near ~2_where~ at ~1_coords~!  New Trade Orders are suspended until Krampus has been defeated!
            }
            else
            {
                City origin = minister.City;
                City destination;

                do
                {
                    destination = CityLoyaltySystem.GetRandomCity();
                }
                while (destination == origin);

                int distance = GetDistance(minister, destination);
                int trades = Utility.RandomMinMax(1, GetMaxTrades(from));
                TradeEntry entry = new TradeEntry(destination, origin, distance);
                TradeOrderCrate crate = new TradeOrderCrate(from, entry);

                GetPlayerEntry<CityTradeEntry>(from as PlayerMobile, true);

                for (int i = 0; i < trades; i++)
                {
                    int worth = 1;
                    string name = null;

                    Type t = GetRandomTrade(origin, destination, ref worth, ref name);

                    if (t != null)
                    {
                        int amount = Utility.RandomList(5, 10, 15, 20);
                        entry.Details.Add(new TradeEntry.TradeDetails(t, worth, amount, name));
                    }
                    else
                    {
                        minister.SayTo(from, "There are no trades available at this time.");
                        return false;
                    }
                }

                if (from.Backpack == null || !from.Backpack.TryDropItem(from, crate, false))
                {
                    crate.Delete();
                    from.SendLocalizedMessage(114456); // Your backpack cannot hold the Trade Order.  Free up space and speak to the Trade Minister again.
                }

                ActiveTrades[from] = crate;

                return true;
            }
			
			return false;
		}
		
		public bool TryTurnIn(Mobile from, TradeOrderCrate order, Mobile turninMobile)
		{
            if (order == null || from == null || turninMobile == null || order.Entry == null)
				return false;
				
			TradeEntry entry = order.Entry;
            TradeMinister minister = turninMobile as TradeMinister;

			if(from.AccessLevel == AccessLevel.Player && minister != null && minister.City != entry.Destination)
                turninMobile.SayTo(from, 1151738, String.Format("#{0}", CityLoyaltySystem.GetCityLocalization(entry.Destination))); // Begging thy pardon, but those goods are destined for the City of ~1_city~
			else if(!order.Fulfilled)
                turninMobile.SayTo(from, 1151732); // This trade order has not been fulfilled.  Fill the trade order with all necessary items and try again.
			else
			{
				CityLoyaltySystem.OnTradeComplete(from, order.Entry);
                CityTradeEntry pentry = GetPlayerEntry<CityTradeEntry>(from as PlayerMobile);

                if (pentry != null)
                {
                    pentry.Points++;
                    pentry.DistanceTraveled += entry.Distance;
                    pentry.Completed++;
                    CheckTitle(pentry);
                }
				
				order.Delete();
                return true;
			}

            return false;
		}

        public bool TryTurnInToSlim(Mobile from, TradeOrderCrate order, SlimTheFence slim)
        {
            if (order == null || from == null || slim == null || order.Entry == null)
                return false;

            TradeEntry entry = order.Entry;

            if (!order.Fulfilled)
                slim.SayTo(from, 1151732); // This trade order has not been fulfilled.  Fill the trade order with all necessary items and try again.
            else
            {
                CityLoyaltySystem.OnSlimTradeComplete(from, order.Entry);
                CityTradeEntry pentry = GetPlayerEntry<CityTradeEntry>(from as PlayerMobile, true);

                if (pentry != null)
                {
                    pentry.Points++;
                    pentry.DistanceTraveled += entry.Distance;
                    pentry.CompletedSlim++;
                    CheckTitle(pentry);
                }

                slim.SayTo(from, 1151736); // Haha! These goods will fetch me quite a bit o' coin!  Thanks fer yer help!  Here's a little something I was able to get me hands on...

                order.Delete();
                return true;
            }

            return false;
        }

        public void CheckTitle(CityTradeEntry entry)
        {
            switch (entry.Completed)
            {
                case 1: entry.Player.AddRewardTitle((int)TradeTitle.Trader); break;
                case 25: entry.Player.AddRewardTitle((int)TradeTitle.Exporter); break;
                case 50: entry.Player.AddRewardTitle((int)TradeTitle.Broker); break;
                case 100: entry.Player.AddRewardTitle((int)TradeTitle.Tycoon); break;
                case 150: entry.Player.AddRewardTitle((int)TradeTitle.Magnate); break;
            }

            if(entry.CompletedSlim == 50)
                entry.Player.AddRewardTitle((int)TradeTitle.Smuggler);
        }

        public override void OnPlayerAdded(PlayerMobile m)
        {
            m.Backpack.DropItem(new MysteriousNote());
            m.PrivateOverheadMessage(Server.Network.MessageType.Regular, 1150, 1151734, m.NetState); // *A passerby slips a rolled bit of parchment into your hand...*
        }
		
		public static void CancelTradeOrder(Mobile from, TradeOrderCrate crate)
		{
            if (from == null)
                from = crate.Owner;

            if (from != null)
            {
                crate.Items.ForEach(i =>
                {
                    from.Backpack.DropItem(i);
                });

                CityTradeEntry entry = CityLoyaltySystem.CityTrading.GetPlayerEntry<CityTradeEntry>(from as PlayerMobile, true);

                if (entry != null)
                    entry.Canceled++;
            }
			
			crate.Delete();
		}

        private Dictionary<Type, string> _NameBuffer;

        public string GetNameFor(Type t, string fallbackname)
        {
            if (_NameBuffer.ContainsKey(t))
                return _NameBuffer[t];

            Item item = Loot.Construct(t);

            if (item != null)
            {
                string name;

                if (item.Name != null)
                {
                    name = item.Name;
                }
                else
                {
                    name = item.LabelNumber.ToString();
                }

                _NameBuffer[t] = name;
                item.Delete();

                return name;
            }

            Console.WriteLine("WARNING: Using Fallback name for: {0}", t.Name);
            return fallbackname;
        }
		
		public void RemoveCrate(Mobile from, TradeOrderCrate crate)
		{
			if(ActiveTrades.ContainsKey(from))
			{
                ActiveTrades.Remove(from);
			}
		}

        public static void OnPublicMoongateUsed(Mobile from)
        {
            if (ActiveTrades.ContainsKey(from))
            {
                ActiveTrades[from].Entry.Distance = 0;
            }
        }
		
		public static int GetDistance(TradeMinister origin, City destination)
		{
            TradeMinister destMinister = TradeMinister.Ministers.FirstOrDefault(m => m.City == destination);
			
			if(destMinister != null)
			{
				return (int)origin.GetDistanceToSqrt(destMinister.Location);
			}
			
			return 0;
		}

        public static Type GetRandomTrade(City originCity, City dest, ref int worth, ref string name)
        {
            Region region = CityLoyaltySystem.GetCityInstance(originCity).Definition.Region;
            List<BaseVendor> list = new List<BaseVendor>(region.GetEnumeratedMobiles().OfType<BaseVendor>().Where(bv => bv.GetBuyInfo() != null && bv.GetBuyInfo().Length > 0));

            if (list.Count == 0)
                return null;

            do
            {
                BaseVendor vendor = list[Utility.Random(list.Count)];
                IBuyItemInfo[] buyInfo = vendor.GetBuyInfo();

                GenericBuyInfo info = buyInfo[Utility.Random(buyInfo.Length)] as GenericBuyInfo;

                if (!(info is BeverageBuyInfo) && !(info is AnimalBuyInfo) && info != null && info.Type != null && info.Args == null && info.Price < 5000)
                {
                    list.Clear();
                    list.TrimExcess();

                    worth = info.Price;
                    name = info.Name;

                    return info.Type;
                }
                else
                    list.Remove(vendor);
            }
            while (list.Count > 0);

            list.Clear();
            list.TrimExcess();
            return null;
        }

        public static void OnTick()
        {
            List<TradeOrderCrate> crates = new List<TradeOrderCrate>(ActiveTrades.Values);
            List<BaseCreature> toDelete = new List<BaseCreature>();

            foreach (var c in crates)
            {
                if (c.Expired)
                {
                    CancelTradeOrder(c.Owner, c);
                }
                else if (c.Entry != null)
                {
                    CheckAmbush(c);
                }
            }

            if (Ambushers != null)
            {
                foreach (KeyValuePair<BaseCreature, DateTime> kvp in Ambushers)
                {
                    if (kvp.Value < DateTime.UtcNow)
                        toDelete.Add(kvp.Key);
                }

                toDelete.ForEach(bc =>
                    {
                        if (!bc.Deleted)
                            bc.Delete();

                        Ambushers.Remove(bc);
                    });
            }

            toDelete.Clear();
            toDelete.TrimExcess();
            crates.Clear();
            crates.TrimExcess();
        }

        public static void CheckAmbush(TradeOrderCrate crate)
        {
            if (crate == null || crate.Deleted || crate.Entry == null || crate.Expired || crate.Entry.LastAmbush + TimeSpan.FromMinutes(AmbushWaitDuration) > DateTime.UtcNow)
                return;

            if (crate.RootParentEntity is Mobile && !((Mobile)crate.RootParentEntity).Region.IsPartOf<GuardedRegion>())
            {
                Mobile m = crate.RootParentEntity as Mobile;

                if (m.NetState != null && m.Map != null && m.Map != Map.Internal)
                {
                    double chance = crate.Entry.LastAmbush == DateTime.MinValue ? 0.25 : .05;

                    if (chance > Utility.RandomDouble())
                    {
                        double dif = (double)(Math.Min(7200, m.SkillsTotal) + m.RawStr + m.RawInt + m.RawDex) / 10000;

                        m.RevealingAction();

                        SpawnCreatures(m, dif);
                        crate.Entry.LastAmbush = DateTime.UtcNow;
                    }
                }
            }
        }

        public static void SpawnCreatures(Mobile m, double difficulty)
        {
            BaseBoat boat = BaseBoat.FindBoatAt(m.Location, m.Map);

            Type[] types = GetCreatureType(m, boat != null);

            if (types == null)
            {
                return;
            }

            int amount = Utility.RandomMinMax(3, 5);

            for (int i = 0; i < amount; i++)
            {
                BaseCreature bc = Activator.CreateInstance(types[Utility.Random(types.Length)]) as BaseCreature;

                if (bc != null)
                {
                    if (KrampusEncounterActive)
                    {
                        bc.Name = "An Icy Creature";
                    }

                    Rectangle2D zone;

                    if (boat != null)
                    {
                        if (boat.Facing == Direction.North || boat.Facing == Direction.South)
                        {
                            if (Utility.RandomBool())
                            {
                                zone = new Rectangle2D(boat.X - 7, m.Y - 4, 3, 3);
                            }
                            else
                            {
                                zone = new Rectangle2D(boat.X + 4, m.Y - 4, 3, 3);
                            }
                        }
                        else
                        {
                            if (Utility.RandomBool())
                            {
                                zone = new Rectangle2D(m.X + 4, boat.Y - 7, 3, 3);
                            }
                            else
                            {
                                zone = new Rectangle2D(m.X + 4, boat.Y + 4, 3, 3);
                            }
                        }
                    }
                    else
                    {
                        zone = new Rectangle2D(m.X - 3, m.Y - 3, 6, 6);
                    }

                    Point3D p = m.Location;

                    if (m.Map != null)
                    {
                        for (int j = 0; j < 25; j++)
                        {
                            Point3D check = m.Map.GetRandomSpawnPoint(zone);

                            if (CanFit(check.X, check.Y, check.Z, m.Map, bc))
                            {
                                p = check;
                                break;
                            }
                        }
                    }

                    foreach (Skill sk in bc.Skills.Where(s => s.Base > 0))
                    {
                        sk.Base += sk.Base * (difficulty);
                    }

                    bc.RawStr += (int)(bc.RawStr * difficulty);
                    bc.RawInt += (int)(bc.RawInt * difficulty);
                    bc.RawDex += (int)(bc.RawDex * difficulty);
                    
                    if (bc.HitsMaxSeed == -1)
                        bc.HitsMaxSeed = bc.RawStr;

                    if (bc.StamMaxSeed == -1)
                        bc.StamMaxSeed = bc.RawDex;

                    if (bc.ManaMaxSeed == -1)
                        bc.ManaMaxSeed = bc.RawInt;

                    bc.HitsMaxSeed += (int)(bc.HitsMaxSeed * difficulty);
                    bc.StamMaxSeed += (int)(bc.StamMaxSeed * difficulty);
                    bc.ManaMaxSeed += (int)(bc.ManaMaxSeed * difficulty);

                    bc.Hits = bc.HitsMaxSeed;
                    bc.Stam = bc.RawDex;
                    bc.Mana = bc.RawInt;

                    bc.PhysicalResistanceSeed += (int)(bc.PhysicalResistanceSeed * (difficulty / 3));
                    bc.FireResistSeed += (int)(bc.FireResistSeed * (difficulty / 3));
                    bc.ColdResistSeed += (int)(bc.ColdResistSeed * (difficulty / 3));
                    bc.PoisonResistSeed += (int)(bc.PoisonResistSeed * (difficulty / 3));
                    bc.EnergyResistSeed += (int)(bc.EnergyResistSeed * (difficulty / 3));

                    bc.IsAmbusher = true;

                    if (Ambushers == null)
                        Ambushers = new Dictionary<BaseCreature, DateTime>();

                    Ambushers.Add(bc, DateTime.UtcNow + TimeSpan.FromMinutes(AmbusherDelete));

                    bc.MoveToWorld(p, m.Map);
                    Timer.DelayCall(() => bc.Combatant = m);
                }
            }

            m.LocalOverheadMessage(Server.Network.MessageType.Regular, 1150, 1155479); // *Your keen senses alert you to an incoming ambush of attackers!*
            m.SendLocalizedMessage(1049330, "", 0x22); // You have been ambushed! Fight for your honor!!!
        }

        public static Type[] GetCreatureType(Mobile m, bool wet)
        {
            if (KrampusEncounterActive)
            {
                return KrampusEncounter.Encounter.GetCreatureTypes(m, wet);
            }

            return wet ? _SeaTypes : _LandTypes;
        }

        public override void ProcessKill(Mobile victim, Mobile damager)
        {
            if (victim is BaseCreature && Ambushers != null && Ambushers.ContainsKey((BaseCreature)victim))
            {
                if (ActiveTrades.ContainsKey(damager))
                {
                    TradeOrderCrate crate = ActiveTrades[damager];

                    if (crate.Entry != null)
                        crate.Entry.Kills++;
                }

                Ambushers.Remove((BaseCreature)victim);
            }
        }

        private static Type[] _SeaTypes =
        {
            typeof(SeaSerpent), typeof(DeepSeaSerpent), typeof(Kraken), typeof(WaterElemental)
        };

        private static Type[] _LandTypes =
        {
            typeof(Troll), typeof(Ettin), typeof(GiantSpider), typeof(Brigand)
        };

        public static bool CanFit(int x, int y, int z, Map map, Mobile mob, int height = 16, bool checkMobiles = true, bool requireSurface = true)
        {
            if (map == null || map == Map.Internal)
                return false;

            if (x < 0 || y < 0 || x >= map.Width || y >= map.Height)
                return false;

            bool hasSurface = false;
            bool canswim = mob.CanSwim;
            bool cantwalk = mob.CantWalk;

            LandTile lt = map.Tiles.GetLandTile(x, y);
            int lowZ = 0, avgZ = 0, topZ = 0;

            bool surface, impassable;

            map.GetAverageZ(x, y, ref lowZ, ref avgZ, ref topZ);
            TileFlag landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags;

            impassable = (landFlags & TileFlag.Impassable) != 0;

            bool wet = (landFlags & TileFlag.Wet) != 0;

            if (cantwalk && !wet)
            {
                impassable = true;
            }

            if (canswim && wet)
            {
                impassable = false;
            }

            if (impassable && avgZ > z && (z + height) > lowZ)
                return false;
            else if (!impassable && z == avgZ && !lt.Ignored)
                hasSurface = true;

            StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);

            for (int i = 0; i < staticTiles.Length; ++i)
            {
                ItemData id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue];
                surface = id.Surface;
                impassable = id.Impassable;

                wet = (id.Flags & TileFlag.Wet) != 0;
                
                if (cantwalk && !wet)
                {
                    impassable = true;
                }
                if (canswim && wet)
                {
                    surface = true;
                    impassable = false;
                }

                if ((surface || impassable) && (staticTiles[i].Z + id.CalcHeight) > z && (z + height) > staticTiles[i].Z)
                    return false;
                else if (surface && !impassable && z == (staticTiles[i].Z + id.CalcHeight))
                    hasSurface = true;
            }

            IPooledEnumerable eable = map.GetItemsInRange(new Point3D(x, y, z), 0);

            foreach(Item item in eable)
            {
                if (item.ItemID < 0x4000)
                {
                    ItemData id = item.ItemData;
                    surface = id.Surface;
                    impassable = id.Impassable;

                    wet = (id.Flags & TileFlag.Wet) != 0;
                    if (cantwalk && !wet)
                    {
                        impassable = true;
                    }
                    if (canswim && wet)
                    {
                        surface = true;
                        impassable = false;
                    }

                    if ((surface || impassable) && (item.Z + id.CalcHeight) > z && (z + height) > item.Z)
                    {
                        eable.Free();
                        return false;
                    }
                    else if (surface && !impassable && !item.Movable && z == (item.Z + id.CalcHeight))
                    {
                        hasSurface = true;
                    }
                }
            }

            eable.Free();

            if (checkMobiles)
            {
                eable = map.GetMobilesInRange(new Point3D(x, y, z), 0);

                foreach(Mobile m in eable)
                {
                    if (m.AccessLevel == AccessLevel.Player || !m.Hidden)
                    {
                        if ((m.Z + 16) > z && (z + height) > m.Z)
                        {
                            eable.Free();
                            return false;
                        }
                    }
                }

                eable.Free();
            }

            return !requireSurface || hasSurface;
        }

        [PropertyObject]
		public class CityTradeEntry : PointsEntry
		{
            [CommandProperty(AccessLevel.GameMaster)]
			public int Canceled { get; set; }

            [CommandProperty(AccessLevel.GameMaster)]
			public int DistanceTraveled { get; set; }

            [CommandProperty(AccessLevel.GameMaster)]
            public int Completed { get; set; }

            [CommandProperty(AccessLevel.GameMaster)]
            public int CompletedSlim { get; set; }
			
			public CityTradeEntry(PlayerMobile pm) : base(pm)
			{
			}

            public override string ToString()
            {
                return "...";
            }
			
			public override void Serialize(GenericWriter writer)
			{
				base.Serialize(writer);
				writer.Write(1);
				
				writer.Write(Canceled);
				writer.Write(DistanceTraveled);
                writer.Write(Completed);

                writer.Write(Ambushers == null ? 0 : Ambushers.Count);
                if (Ambushers != null)
                {
                    foreach (KeyValuePair<BaseCreature, DateTime> kvp in Ambushers)
                    {
                        writer.Write(kvp.Key);
                        writer.Write(kvp.Value);
                    }
                }
			}
			
			public override void Deserialize(GenericReader reader)
			{
				base.Deserialize(reader);
				int version = reader.ReadInt();
				
				Canceled = reader.ReadInt();
				DistanceTraveled = reader.ReadInt();
                Completed = reader.ReadInt();

                int count = reader.ReadInt();
                for (int i = 0; i < count; i++)
                {
                    BaseCreature bc = reader.ReadMobile() as BaseCreature;
                    DateTime dt = reader.ReadDateTime();

                    if (bc != null)
                    {
                        if (dt < DateTime.UtcNow)
                            bc.Delete();
                        else
                        {
                            if (Ambushers == null)
                                Ambushers = new Dictionary<BaseCreature, DateTime>();

                            bc.IsAmbusher = true;

                            Ambushers[bc] = dt;
                        }
                    }
                }

                if (version == 0)
                {
                    Timer.DelayCall(() =>
                        {
                            if (Player.RemoveRewardTitle(2303807, true))
                                Player.AddRewardTitle(1151739);
                        });
                }
			}
		}
		
		public override void Serialize(GenericWriter writer)
		{
			base.Serialize(writer);
			writer.Write(0);
			
			writer.Write(ActiveTrades.Count);
            foreach (KeyValuePair<Mobile, TradeOrderCrate> kvp in ActiveTrades)
            {
                writer.Write(kvp.Key);
                writer.Write(kvp.Value);
            }

            writer.Write(_NameBuffer.Count);
            foreach (KeyValuePair<Type, string> kvp in _NameBuffer)
            {
                writer.Write(kvp.Key.Name);
                writer.Write(kvp.Value);
            }
		}
		
		public override void Deserialize(GenericReader reader)
		{
			base.Deserialize(reader);
			int version = reader.ReadInt();
			
			int count = reader.ReadInt();
            for (int i = 0; i < count; i++)
            {
                Mobile m = reader.ReadMobile();
                TradeOrderCrate crate = reader.ReadItem() as TradeOrderCrate;

                if (m != null && crate != null)
                    ActiveTrades[m] = crate;
            }

            _NameBuffer = new Dictionary<Type, string>();

            count = reader.ReadInt();
            for (int i = 0; i < count; i++)
            {
                Type t = ScriptCompiler.FindTypeByName(reader.ReadString());
                string name = reader.ReadString();

                if (t != null)
                    _NameBuffer[t] = name;
            }
		}
	}
}
