using System;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;

namespace Server.Engines.XmlSpawner2
{
    public class XmlUse : XmlAttachment
    {
        private bool m_BlockDefaultUse;
        private string m_Condition;         // additional condition required for use
        private string m_TargetingAction;    // action performed when the target cursor is brought up
        private string m_TargetCondition;   // condition test applied when target is selected to determine whether it is appropriate
        private string m_TargetFailureAction;     // action performed if target condition is not met
        private string m_SuccessAction;     // action performed on successful use or targeting
        private string m_FailureAction;     // action performed if the player cannot use the object for reasons other than range, refractory, or maxuses
        private string m_RefractoryAction;  // action performed if the object is used before the refractory interval expires
        private string m_MaxUsesAction;     // action performed if the object is used when the maxuses are exceeded
        private int m_NUses = 0;
        private int m_MaxRange = 3;         // must be within 3 tiles to use by default
        private int m_MaxTargetRange = 30;         // must be within 30 tiles to target by default
        private int m_MaxUses = 0;
        private TimeSpan m_Refractory = TimeSpan.Zero;
        public DateTime m_EndTime;
        private bool m_RequireLOS = false;
        private bool m_AllowCarried = true;
        private bool m_TargetingEnabled = false;


        [CommandProperty(AccessLevel.GameMaster)]
        public bool TargetingEnabled { get { return m_TargetingEnabled; } set { m_TargetingEnabled = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool AllowCarried { get { return m_AllowCarried; } set { m_AllowCarried = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool RequireLOS { get { return m_RequireLOS; } set { m_RequireLOS = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int MaxRange { get { return m_MaxRange; } set { m_MaxRange = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int MaxTargetRange { get { return m_MaxTargetRange; } set { m_MaxTargetRange = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int NUses { get { return m_NUses; } set { m_NUses = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int MaxUses { get { return m_MaxUses; } set { m_MaxUses = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan Refractory { get { return m_Refractory; } set { m_Refractory = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool BlockDefaultUse { get { return m_BlockDefaultUse; } set { m_BlockDefaultUse = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string Condition { get { return m_Condition; } set { m_Condition = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string TargetCondition { get { return m_TargetCondition; } set { m_TargetCondition = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string TargetingAction { get { return m_TargetingAction; } set { m_TargetingAction = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string TargetFailureAction { get { return m_TargetFailureAction; } set { m_TargetFailureAction = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string SuccessAction { get { return m_SuccessAction; } set { m_SuccessAction = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string FailureAction { get { return m_FailureAction; } set { m_FailureAction = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string RefractoryAction { get { return m_RefractoryAction; } set { m_RefractoryAction = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public string MaxUsesAction { get { return m_MaxUsesAction; } set { m_MaxUsesAction = value; } }

        public XmlUse(ASerial serial)
            : base(serial)
        {
        }
        [Attachable]
        public XmlUse()
        {
        }

        [Attachable]
        public XmlUse(int maxuses)
        {
            MaxUses = maxuses;
        }

        [Attachable]
        public XmlUse(int maxuses, double refractory)
        {
            MaxUses = maxuses;
            Refractory = TimeSpan.FromSeconds(refractory);
        }

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

            writer.Write(3);
            // version 3
            writer.Write(m_MaxTargetRange);
            // version 2
            writer.Write(m_TargetingEnabled);
            writer.Write(m_TargetingAction);
            writer.Write(m_TargetCondition);
            writer.Write(m_TargetFailureAction);
            // version 1
            writer.Write(m_AllowCarried);
            // version 0
            writer.Write(m_RequireLOS);
            writer.Write(m_MaxRange);
            writer.Write(m_Refractory);
            writer.Write(m_EndTime - DateTime.Now);
            writer.Write(m_MaxUses);
            writer.Write(m_NUses);
            writer.Write(m_BlockDefaultUse);
            writer.Write(m_Condition);
            writer.Write(m_SuccessAction);
            writer.Write(m_FailureAction);
            writer.Write(m_RefractoryAction);
            writer.Write(m_MaxUsesAction);
        }

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

            int version = reader.ReadInt();
            switch (version)
            {
                case 3:
                    m_MaxTargetRange = reader.ReadInt();
                    goto case 2;
                case 2:
                    m_TargetingEnabled = reader.ReadBool();
                    m_TargetingAction = reader.ReadString();
                    m_TargetCondition = reader.ReadString();
                    m_TargetFailureAction = reader.ReadString();
                    goto case 1;
                case 1:
                    m_AllowCarried = reader.ReadBool();
                    goto case 0;
                case 0:
                    // version 0
                    m_RequireLOS = reader.ReadBool();
                    m_MaxRange = reader.ReadInt();
                    Refractory = reader.ReadTimeSpan();
                    TimeSpan remaining = reader.ReadTimeSpan();
                    m_EndTime = DateTime.Now + remaining;
                    m_MaxUses = reader.ReadInt();
                    m_NUses = reader.ReadInt();
                    m_BlockDefaultUse = reader.ReadBool();
                    m_Condition = reader.ReadString();
                    m_SuccessAction = reader.ReadString();
                    m_FailureAction = reader.ReadString();
                    m_RefractoryAction = reader.ReadString();
                    m_MaxUsesAction = reader.ReadString();
                    break;
            }
        }

        public void ExecuteActions(Mobile mob, object target, string actions)
        {
            if (actions == null || actions.Length <= 0) return;
            // execute any action associated with it
            // allow for multiple action strings on a single line separated by a semicolon

            string[] args = actions.Split(';');

            for (int j = 0; j < args.Length; j++)
            {
                ExecuteAction(mob, target, args[j]);
            }

        }

        private void ExecuteAction(Mobile mob, object target, string action)
        {
            if (action == null || action.Length <= 0) return;

            string status_str = null;
            Server.Mobiles.XmlSpawner.SpawnObject TheSpawn = new Server.Mobiles.XmlSpawner.SpawnObject(null, 0);

            TheSpawn.TypeName = action;
            string substitutedtypeName = BaseXmlSpawner.ApplySubstitution(null, target, mob, action);
            string typeName = BaseXmlSpawner.ParseObjectType(substitutedtypeName);

            Point3D loc = new Point3D(0, 0, 0);
            Map map = null;


            if (target is Item)
            {
                Item ti = target as Item;
                if (ti.Parent == null)
                {
                    loc = ti.Location;
                    map = ti.Map;
                }
                else if (ti.RootParent is Item)
                {
                    loc = ((Item)ti.RootParent).Location;
                    map = ((Item)ti.RootParent).Map;
                }
                else if (ti.RootParent is Mobile)
                {
                    loc = ((Mobile)ti.RootParent).Location;
                    map = ((Mobile)ti.RootParent).Map;
                }

            }
            else if (target is Mobile)
            {
                Mobile ti = target as Mobile;

                loc = ti.Location;
                map = ti.Map;

            }

            if (BaseXmlSpawner.IsTypeOrItemKeyword(typeName))
            {
                BaseXmlSpawner.SpawnTypeKeyword(target, TheSpawn, typeName, substitutedtypeName, true, mob, loc, map, out status_str);
            }
            else
            {
                // its a regular type descriptor so find out what it is
                Type type = SpawnerType.GetType(typeName);
                try
                {
                    string[] arglist = BaseXmlSpawner.ParseString(substitutedtypeName, 3, "/");
                    object o = Server.Mobiles.XmlSpawner.CreateObject(type, arglist[0]);

                    if (o == null)
                    {
                        status_str = "invalid type specification: " + arglist[0];
                    }
                    else
                        if (o is Mobile)
                        {
                            Mobile m = (Mobile)o;
                            if (m is BaseCreature)
                            {
                                BaseCreature c = (BaseCreature)m;
                                c.Home = loc; // Spawners location is the home point
                            }

                            m.Location = loc;
                            m.Map = map;

                            BaseXmlSpawner.ApplyObjectStringProperties(null, substitutedtypeName, m, mob, target, out status_str);
                        }
                        else
                            if (o is Item)
                            {
                                Item item = (Item)o;
                                BaseXmlSpawner.AddSpawnItem(null, target, TheSpawn, item, loc, map, mob, false, substitutedtypeName, out status_str);
                            }
                }
                catch { }
            }

            ReportError(mob, status_str);
        }

        private void ReportError(Mobile mob, string status_str)
        {
            if (status_str != null && mob != null && !mob.Deleted && mob is PlayerMobile && mob.AccessLevel > AccessLevel.Player)
            {
                mob.SendMessage(33, String.Format("{0}:{1}", Name, status_str));
            }
        }

        // return true to allow use
        private bool CheckCondition(Mobile from, object target)
        {
            // test the condition if there is one
            if (Condition != null && Condition.Length > 0)
            {
                string status_str;

                return BaseXmlSpawner.CheckPropertyString(null, target, Condition, from, out status_str);
            }

            return true;
        }

        // return true to allow use
        private bool CheckTargetCondition(Mobile from, object target)
        {
            // test the condition if there is one
            if (TargetCondition != null && TargetCondition.Length > 0)
            {
                string status_str;

                return BaseXmlSpawner.CheckPropertyString(null, target, TargetCondition, from, out status_str);
            }

            return true;
        }

        // return true to allow use
        private bool CheckRange(Mobile from, object target)
        {
            if (from == null || !(target is IEntity) || MaxRange < 0) return false;

            Map map = ((IEntity)target).Map;
            Point3D loc = ((IEntity)target).Location;

            if (map != from.Map) return false;

            // check for allowed use in pack
            if (target is Item)
            {
                Item targetitem = (Item)target;
                // is it carried by the user?
                if (targetitem.RootParent == from)
                {
                    return AllowCarried;
                }
                else
                    // block use in other containers or on other mobiles
                    if (targetitem.Parent != null)
                    {
                        return false;
                    }
            }

            bool haslos = true;
            if (RequireLOS)
            {
                // check los as well
                haslos = from.InLOS(target);
            }

            return from.InRange(loc, MaxRange) && haslos;
        }

        public bool CheckMaxUses
        {
            get
            {
                // is there a use limit?
                if (MaxUses > 0 && NUses >= MaxUses) return false;

                return true;
            }
        }

        public bool CheckRefractory
        {
            get
            {
                // is there a refractory limit?
                // if it is still refractory then return
                if (Refractory > TimeSpan.Zero && DateTime.Now < m_EndTime) return false;

                return true;
            }
        }

        public void OutOfRange(Mobile from)
        {
            if (from == null) return;

            from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
        }

        public class XmlUseTarget : Target
        {
            private object m_objectused;
            private readonly XmlUse m_xa;

            public XmlUseTarget(int range, object objectused, XmlUse xa)
                : base(range, true, TargetFlags.None)
            {
                m_objectused = objectused;
                m_xa = xa;
                CheckLOS = false;
            }
            protected override void OnTarget(Mobile from, object targeted)
            {
                if (from == null || targeted == null || m_xa == null) return;

                // success
                if (m_xa.CheckTargetCondition(from, targeted))
                {
                    m_xa.ExecuteActions(from, targeted, m_xa.SuccessAction);

                    m_xa.m_EndTime = DateTime.Now + m_xa.Refractory;
                    m_xa.NUses++;
                }
                else
                {
                    m_xa.ExecuteActions(from, targeted, m_xa.TargetFailureAction);
                }

            }
        }

        private void TryToTarget(Mobile from, object target, XmlUse xa)
        {
            if (from == null) return;

            ExecuteActions(from, target, TargetingAction);

            if (xa != null)
            {
                from.Target = new XmlUseTarget(xa.MaxTargetRange, target, xa);
            }
        }

        private void TryToUse(Mobile from, object target)
        {
            if (CheckRange(from, target) && CheckCondition(from, target) && CheckMaxUses && CheckRefractory)
            {
                // check for targeting
                if (TargetingEnabled)
                {
                    TryToTarget(from, target, this);
                }
                else
                {
                    // success
                    ExecuteActions(from, target, SuccessAction);

                    m_EndTime = DateTime.Now + Refractory;
                    NUses++;
                }
            }
            else
            {
                // failure
                if (!CheckRange(from, target))
                {
                    OutOfRange(from);
                }
                else if (!CheckRefractory)
                {
                    ExecuteActions(from, target, RefractoryAction);
                }
                else if (!CheckMaxUses)
                {
                    ExecuteActions(from, target, MaxUsesAction);
                }

                else
                {
                    ExecuteActions(from, target, FailureAction);
                }
            }
        }

        // disable the default use of the target
        public override bool BlockDefaultOnUse(Mobile from, object target)
        {
            return (BlockDefaultUse || !(CheckRange(from, target) && CheckCondition(from, target) && CheckMaxUses && CheckRefractory));
        }

        // this is called when the attachment is on the user
        public override void OnUser(object target)
        {
            Mobile from = AttachedTo as Mobile;

            TryToUse(from, target);

        }


        // this is called when the attachment is on the target being used
        public override void OnUse(Mobile from)
        {
            object target = AttachedTo;

            // if a target tries to use itself, then ignore it, it will be handled by OnUser
            if (target == from) return;

            TryToUse(from, target);
        }
    }
}
