/*
 * 
 * Phasing System
 * Beta Version 2.6
 * Designed for SVN 663 + ML
 * 
 * Authored by Dougan Ironfist
 * Last Updated on 3/7/2011
 *
 * The purpose of these scripts is to allow shard administrators to create phasing abilities for quests.
 * This simulates the phasing abilities of games like World of Warcraft.
 * 
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Server;
using Server.Commands;
using Server.Items;

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

namespace Server.Phasing
{
    public class GenPhases
    {
        private static Mobile m_Mobile;
        private static int m_Count;

        public static void Initialize()
        {
            CommandSystem.Register("GenPhases", AccessLevel.Administrator, new CommandEventHandler(GenPhases_OnCommand));
            CommandSystem.Register("GP", AccessLevel.Administrator, new CommandEventHandler(GenPhases_OnCommand));
        }

        [Usage("GenPhases")]
        [Aliases("GP")]
        [Description("Initialize the phasing system.")]
        private static void GenPhases_OnCommand(CommandEventArgs e)
        {
            m_Mobile = e.Mobile;
            m_Count = 0;

            m_Mobile.SendMessage("Generating phase systems, please wait.");
            Generate("Data/Phasing");

            m_Mobile.SendMessage("Phase system generating complete. {0} items were generated.", m_Count);
        }

        public static void Generate(string folder)
        {
            if (!Directory.Exists(folder))
                return;

            string[] files = Directory.GetFiles(folder, "*.xml", SearchOption.TopDirectoryOnly);

            for (int i = 0; i < files.Length; ++i)
            {
                List<XmlItemDefinition> list = ReadAll(files[i]);

                for (int j = 0; j < list.Count; ++j)
                    m_Count += Build(list[j]);
            }
        }

        private static int Build(XmlItemDefinition definition)
        {
            int count = 0;

            Item item = Construct(definition);

            if (item != null)
            {
                if (!FindItem(definition.Location, definition.Map, item))
                {
                    item.MoveToWorld(definition.Location, definition.Map);
                    ++count;

                    if (item is BaseDoor)
                    {
                        IPooledEnumerable eable = definition.Map.GetItemsInRange(definition.Location, 1);

                        Type itemType = item.GetType();

                        foreach (Item link in eable)
                        {
                            if (link != item && link.Z == item.Z && link.GetType() == itemType)
                            {
                                ((BaseDoor)item).Link = (BaseDoor)link;
                                ((BaseDoor)link).Link = (BaseDoor)item;
                                break;
                            }
                        }

                        eable.Free();
                    }

                    item = null;
                }
            }

            if (item != null)
                item.Delete();

            return count;
        }

        private static Item Construct(XmlItemDefinition definition)
        {
            Item item = null;

            try
            {
                item = (Item)Activator.CreateInstance(definition.ItemType);

                if (item is IPhasable)
                {
                    if (definition.ItemID >= 0)
                        item.ItemID = definition.ItemID;

                    if (definition.Hue > 0)
                        item.Hue = definition.Hue;

                    ((IPhasable)item).AssignPhasing(definition.System, definition.StartPhase, definition.EndPhase);
                }
                else
                    item = null;
            }
            catch (Exception e)
            {
                throw new Exception(String.Format("Bad phasing type: {0}", definition.ItemType), e);
            }

            return item;
        }

        private static Queue m_DeleteQueue = new Queue();

        private static bool FindItem(Point3D location, Map map, Item srcItem)
        {
            int itemID = srcItem.ItemID;

            bool res = false;

            IPooledEnumerable eable;

            if (srcItem is BaseDoor)
            {
                eable = map.GetItemsInRange(location, 1);

                foreach (Item item in eable)
                {
                    if (!(item is BaseDoor))
                        continue;

                    BaseDoor bd = (BaseDoor)item;
                    Point3D p;
                    int bdItemID;

                    if (bd.Open)
                    {
                        p = new Point3D(bd.X - bd.Offset.X, bd.Y - bd.Offset.Y, bd.Z - bd.Offset.Z);
                        bdItemID = bd.ClosedID;
                    }
                    else
                    {
                        p = bd.Location;
                        bdItemID = bd.ItemID;
                    }

                    if (p.X != location.X || p.Y != location.Y)
                        continue;

                    if (item.Z == location.Z && bdItemID == itemID)
                        res = true;
                    else if (Math.Abs(item.Z - location.Z) < 8)
                        m_DeleteQueue.Enqueue(item);
                }
            }
            else
            {
                eable = map.GetItemsInRange(location, 0);

                foreach (Item item in eable)
                {
                    if (item.Z == location.Z && item.ItemID == itemID)
                    {
                        eable.Free();
                        return true;
                    }
                }
            }

            eable.Free();

            while (m_DeleteQueue.Count > 0)
                ((Item)m_DeleteQueue.Dequeue()).Delete();

            return res;
        }

        private static List<XmlItemDefinition> ReadAll(string path)
        {
            List<XmlItemDefinition> list = new List<XmlItemDefinition>();

            try
            {
                XmlTextReader reader = new XmlTextReader(path);

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            if (reader.Name == "Item")
                            {
                                Type type = null;
                                int itemID = -1;
                                string system = null;
                                int startPhase = -1;
                                int endPhase = -1;
                                Map map = Map.Trammel;
                                int hue = 0;
                                int x = 0;
                                int y = 0;
                                int z = 0;

                                if (reader.HasAttributes)
                                {
                                    while (reader.MoveToNextAttribute())
                                    {
                                        switch (reader.Name)
                                        {
                                            case "Type":
                                                try { type = ScriptCompiler.FindTypeByName(reader.Value); }
                                                catch { type = null; }

                                                break;
                                            case "ItemID":
                                                try { itemID = Convert.ToInt32(reader.Value); }
                                                catch { itemID = -1; }

                                                break;
                                            case "System":
                                                system = reader.Value;

                                                break;
                                            case "StartPhase":
                                                try { startPhase = Convert.ToInt32(reader.Value); }
                                                catch { startPhase = -1; }

                                                break;
                                            case "EndPhase":
                                                try { endPhase = Convert.ToInt32(reader.Value); }
                                                catch { endPhase = -1; }

                                                break;
                                            case "Map":
                                                try { map = Map.Parse(reader.Value); }
                                                catch { map = Map.Trammel; }

                                                break;
                                            case "Hue":
                                                try { hue = Convert.ToInt32(reader.Value); }
                                                catch { hue = 0; }

                                                break;
                                            case "X":
                                                try { x = Convert.ToInt32(reader.Value); }
                                                catch { x = 0; }

                                                break;
                                            case "Y":
                                                try { y = Convert.ToInt32(reader.Value); }
                                                catch { y = 0; }

                                                break;
                                            case "Z":
                                                try { z = Convert.ToInt32(reader.Value); }
                                                catch { z = 0; }

                                                break;
                                        }
                                    }

                                    if (type != null && system != null)
                                        list.Add(new XmlItemDefinition(type, itemID, system, startPhase, endPhase, map, hue, new Point3D(x, y, z)));
                                }
                            }

                            break;
                    }
                }

                reader.Close();
            }
            catch { }

            return list;
        }

        private class XmlItemDefinition
        {
            public Type ItemType;
            public int ItemID;
            public string System;
            public int StartPhase;
            public int EndPhase;
            public Map Map;
            public int Hue;
            public Point3D Location;

            public XmlItemDefinition(Type itemType, int itemID, string system, int startPhase, int endPhase, Map map, int hue, Point3D location)
            {
                ItemType = itemType;
                ItemID = itemID;
                System = system;
                StartPhase = startPhase;
                EndPhase = endPhase;
                Map = map;
                Hue = hue;
                Location = location;
            }
        }
    }
}