/*
 * 
 * Reputation System
 * Beta Version 2.3
 * Designed for SVN 663 + ML
 * 
 * Authored by Dougan Ironfist
 * Last Updated on 3/5/2011
 * 
 * The purpose of these scripts is to allow shard administrators to create reputation groups that allow players to complete
 * quests, kill specific creatures and purchase items from reputation vendors based on their current reputation level.
 * This system is very similiar to how reputation systems work in other games.
 * 
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Gumps;
using Server.Prompts;
using Server.Targeting;
using Server.Misc;
using Server.Multis;
using Server.ContextMenus;
using Server.Reputation;
using Server.Reputation.Items;

/// THIS IS A CORE SCRIPT AND SHOULD NOT BE ALTERED ///

namespace Server.Reputation.Vendor
{
    [AttributeUsage(AttributeTargets.Class)]
    public class ReputationVendorTargetAttribute : Attribute
    {
        public ReputationVendorTargetAttribute()
        {
        }
    }

    public abstract class BaseReputationVendor : BaseVendor
    {
        protected override List<SBInfo> SBInfos { get { return new List<SBInfo>(); } }

        public override bool IsActiveVendor { get { return false; } }
        public override bool IsInvulnerable { get { return true; } }
        public override bool DisallowAllMoves { get { return true; } }
        public override bool ClickTitle { get { return false; } }
        public override bool CanTeach { get { return false; } }

        public abstract BaseReputationGroup RepGroup { get; }
        public abstract string GroupTitle { get; }

        public Hashtable m_SellItems;

        public BaseReputationVendor()
            : base("")
        {
            m_SellItems = new Hashtable();

            AddReputationLogbook();
            RestockReputationItems();
        }

        public BaseReputationVendor(Serial serial)
            : base(serial)
        {
        }

        public override void AddNameProperties(ObjectPropertyList list)
        {
            base.AddNameProperties(list);

            if (GroupTitle != null && GroupTitle.Trim() != "")
                list.Add(GroupTitle);
        }

        public abstract void RestockReputationItems();

        public ReputationBox AddReputationBox(ReputationLevel reputationRequired)
        {
            try
            {
                ReputationBackpack backpack = (ReputationBackpack)this.Backpack;

                ReputationBox box = new ReputationBox(reputationRequired);
                backpack.AddItem(box);

                SetVendorItem(box, 0, reputationRequired);
                return box;
            }
            catch { }

            return null;
        }

        public void AddReputationItem(Item item, int price, Point3D location, ReputationBox vendorBox)
        {
            vendorBox.AddItem(item);
            item.Location = location;

            SetVendorItem(item, price, vendorBox.RequiredReputation);
        }

        private void AddReputationLogbook()
        {
            try
            {
                ReputationBackpack backpack = (ReputationBackpack)this.Backpack;
                ReputationLogbook logBook = new ReputationLogbook();

                backpack.AddItem(logBook);
                logBook.Location = new Point3D(142, 127, 0);

                SetVendorItem(logBook, 5, ReputationLevel.Hated);
            }
            catch { }
        }

        public override void InitBody()
        {
            base.InitBody();

            Hue = Utility.RandomSkinHue();
            SpeechHue = 0x3B2;

            if (!Core.AOS)
                NameHue = 0x35;

            if (this.Female = Utility.RandomBool())
            {
                this.Body = 0x191;
                this.Name = NameList.RandomName("female");
            }
            else
            {
                this.Body = 0x190;
                this.Name = NameList.RandomName("male");
            }
        }

        public override void InitOutfit()
        {
            int hairHue = GetHairHue();

            Utility.AssignRandomHair(this, hairHue);
            Utility.AssignRandomFacialHair(this, hairHue);

            Container pack = new ReputationBackpack();
            pack.Movable = false;
            AddItem(pack);
        }

        protected void EquipItem(Item item, int hue)
        {
            item.Hue = hue;
            EquipItem(item);
        }

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

            if (item is ReputationBox)
                return true;

            from.SendLocalizedMessage(500447); // That is not accessible.
            return false;
        }

        protected List<Item> GetItems()
        {
            List<Item> list = new List<Item>();

            foreach (Item item in this.Items)
                if (item.Movable && item != this.Backpack && item.Layer != Layer.Hair && item.Layer != Layer.FacialHair)
                    list.Add(item);

            if (this.Backpack != null)
                list.AddRange(this.Backpack.Items);

            return list;
        }

        public override bool IsSnoop(Mobile from)
        {
            return false;
        }

        public override void InitSBInfo()
        {
        }

        public ReputationItem GetVendorItem(Item item)
        {
            return (ReputationItem)m_SellItems[item];
        }

        public ReputationItem SetVendorItem(Item item, int price, ReputationLevel reputationRequired)
        {
            ReputationItem ri = new ReputationItem(item, price, reputationRequired);
            m_SellItems[item] = ri;

            item.InvalidateProperties();

            return ri;
        }

        public override bool OnDragDrop(Mobile from, Item item)
        {
            return false;
        }

        public override bool CheckNonlocalDrop(Mobile from, Item item, Item target)
        {
            return false;
        }

        public override bool CheckNonlocalLift(Mobile from, Item item)
        {
            SayTo(from, 503223); // If you'd like to purchase an item, just ask.
            return false;
        }

        public bool CanInteractWith(Mobile from, bool ownerOnly)
        {
            if (!from.InRange(this.Location, 2) || from.Deleted || !from.Alive || !this.CanSee(from))
                return false;

            return true;
        }

        public override void OnDoubleClick(Mobile from)
        {
            if (CanInteractWith(from, false))
                OpenBackpack(from);
            else
                base.OnDoubleClick(from);
        }

        public override void DisplayPaperdollTo(Mobile m)
        {
            if (CanInteractWith(m, false))
                OpenBackpack(m);
            else
                base.DisplayPaperdollTo(m);
        }

        public void OpenBackpack(Mobile from)
        {
            if (this.Backpack != null)
            {
                SayTo(from, 500186); // Greetings.  Have a look around.
                this.Backpack.DisplayTo(from);
            }
        }

        public static void TryToBuy(Item item, Mobile from)
        {
            BaseReputationVendor vendor = item.RootParent as BaseReputationVendor;

            if (vendor == null || !vendor.CanInteractWith(from, false))
                return;

            ReputationItem ri = vendor.GetVendorItem(item);

            if (ri == null)
            {
                vendor.SayTo(from, 503216); // You can't buy that.
            }
            else if (!ri.IsForSale)
            {
                vendor.SayTo(from, 503202); // This item is not for sale.
            }
            else
            {
                if (!ri.Item.IsChildOf(vendor.Backpack))
                {
                    vendor.SayTo(from, 503216); // You can't buy that.
                    return;
                }

                int totalGold = 0;

                if (from.Backpack != null)
                    totalGold += from.Backpack.GetAmount(typeof(Gold));

                totalGold += Banker.GetBalance(from);

                Item dupedItem = DupeItem(vendor, ri.Item);

                if (dupedItem == null)
                {
                    vendor.SayTo(from, 503216); // You can't buy that.
                }
                else if (totalGold < ri.Price && from.AccessLevel < AccessLevel.Administrator)
                {
                    vendor.SayTo(from, 503205); // You cannot afford this item.
                }
                else if (!from.PlaceInBackpack(dupedItem))
                {
                    vendor.SayTo(from, 503204); // You do not have room in your backpack for this.
                }
                else
                {
                    if (from.AccessLevel >= AccessLevel.Administrator)
                        return;

                    int leftPrice = ri.Price;

                    if (from.Backpack != null)
                        leftPrice -= from.Backpack.ConsumeUpTo(typeof(Gold), leftPrice);

                    if (leftPrice > 0)
                        Banker.Withdraw(from, leftPrice);
                }
            }
        }

        private static Item DupeItem(BaseReputationVendor vendor, Item item)
        {
            try
            {
                Type t = item.GetType();

                ConstructorInfo c = t.GetConstructor(Type.EmptyTypes);

                object o = c.Invoke(null);

                if (o != null && o is Item)
                {
                    Item newItem = (Item)o;
                    Server.Commands.Dupe.CopyProperties(newItem, item);
                    item.OnAfterDuped(newItem);
                    newItem.Parent = null;
                    newItem.InvalidateProperties();

                    return newItem;
                }
            }
            catch
            {
                return vendor.ManualDupe(item);
            }

            return null;
        }

        public abstract Item ManualDupe(Item item);

        public override bool HandlesOnSpeech(Mobile from)
        {
            return (from.Alive && from.GetDistanceToSqrt(this) <= 3);
        }

        public bool WasNamed(string speech)
        {
            return this.Name != null && Insensitive.StartsWith(speech, this.Name);
        }

        public override void OnSpeech(SpeechEventArgs e)
        {
            Mobile from = e.Mobile;

            if (e.Handled || !from.Alive || from.GetDistanceToSqrt(this) > 3)
                return;

            if (e.HasKeyword(0x3C) || (e.HasKeyword(0x171) && WasNamed(e.Speech))) // vendor buy, *buy*
            {
                from.SendLocalizedMessage(503213); // Select the item you wish to buy.
                from.Target = new RVBuyTarget();

                e.Handled = true;
            }
            else if (e.HasKeyword(0x3D) || (e.HasKeyword(0x172) && WasNamed(e.Speech))) // vendor browse, *browse
            {
                if (WasNamed(e.Speech))
                    OpenBackpack(from);
                else
                {
                    IPooledEnumerable mobiles = e.Mobile.GetMobilesInRange(2);

                    foreach (Mobile m in mobiles)
                        if (m is PlayerVendor && m.CanSee(e.Mobile) && m.InLOS(e.Mobile))
                            ((PlayerVendor)m).OpenBackpack(from);

                    mobiles.Free();
                }

                e.Handled = true;
            }
        }

        [ReputationVendorTarget]
        private class RVBuyTarget : Target
        {
            public RVBuyTarget()
                : base(3, false, TargetFlags.None)
            {
                AllowNonlocal = true;
            }

            protected override void OnTarget(Mobile from, object targeted)
            {
                if (targeted is Item)
                {
                    TryToBuy((Item)targeted, from);
                }
            }
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            writer.Write((int)0); // version
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            int version = reader.ReadInt();

            m_SellItems = new Hashtable();

            if (Core.AOS && NameHue == 0x35)
                NameHue = -1;
        }
    }
}