using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using PXP.Crawler.API.Utils;

namespace PXP.Crawler.API.Model
{
    [Serializable]
    public class ProjectItem {
        #region Declarations

        public enum ProjectItemType
        {
            Other,
            Project,
            Folder,
            Program,
            Script
        }

        #endregion

        #region Variables

        internal Project project;
        internal string name = "Other";
        internal ListEx<ProjectItem> items = new ListEx<ProjectItem>();
        internal ProjectItemType type = ProjectItemType.Other;
        internal ProjectItem parent = null;
        internal bool rootNotify = false;
        internal object projectObject = null;
        internal int projectObjectID = -1;
        internal string data = "";
        internal string path = "";
        internal int startIndex = -1;

        #endregion

        #region Constructor

        public ProjectItem()
        {            
            items.Tag = this;
            items.Changed += ItemsOnChanged;
            items.Added += ItemsOnAdded;
            items.Removed += ItemsOnRemoved;
        }

        #endregion

        #region Events

        public event ListEx<ProjectItem>.ListExItemEventHandler Changed;

        internal void OnChanged(object sender, ProjectItem chItem)
        {
            if (Changed != null)
                Changed(sender, new ListEx<ProjectItem>.ListExItemEventArgs<ProjectItem>(chItem));

            if (project != null)
            {
                if (this != project.rootProjectItem)
                {
                    project.rootProjectItem.rootNotify = true;
                    project.rootProjectItem.OnChanged(sender, chItem);
                    project.rootProjectItem.rootNotify = false;
                }                
            }
        }

        #endregion

        #region Internal Events

        private void ItemsOnRemoved(object sender, ListEx<ProjectItem>.ListExItemEventArgs<ProjectItem> listExItemEventArgs)
        {
            if (!rootNotify)
            {
                listExItemEventArgs.Item.project = null;
                listExItemEventArgs.Item.parent = null;                
            }

            if (this != project.rootProjectItem)
            {
                project.rootProjectItem.rootNotify = true;
                project.rootProjectItem.items.OnRemoved(listExItemEventArgs.Item);
                project.rootProjectItem.rootNotify = false;
            }
        }

        private void ItemsOnAdded(object sender, ListEx<ProjectItem>.ListExItemEventArgs<ProjectItem> listExItemEventArgs)
        {
            if (!rootNotify)
            {
                listExItemEventArgs.Item.project = project;
                listExItemEventArgs.Item.parent = this;
            }

            if (project != null)
            {
                if (this != project.rootProjectItem)
                {
                    project.rootProjectItem.rootNotify = true;
                    project.rootProjectItem.items.OnAdded(listExItemEventArgs.Item);
                    project.rootProjectItem.rootNotify = false;
                }                
            }
        }

        private void ItemsOnChanged(object sender, EventArgs eventArgs)
        {            
            OnChanged(sender, this);

            if (project != null)
            {
                if (this != project.rootProjectItem)
                {
                    project.rootProjectItem.rootNotify = true;
                    project.rootProjectItem.items.OnChanged();
                    project.rootProjectItem.rootNotify = false;
                }                
            }
        }

        #endregion
        
        #region Properties

        public string Name
        {
            get { return name; }
            set {
                name = value;
                if (project != null)
                {
                    if (this == project.rootProjectItem)
                    {
                        if (project.Name != name)
                        {
                            project.Name = name;                            
                        }
                    }
                    OnChanged(this, this);
                }
                if (projectObject is Program)
                {
                    ((Program) projectObject).Name = name;
                }
            }
        }

        public ProjectItemType Type
        {
            get
            {
                return type;
            }
            set
            {
                type = value;
            }
        }

        public Project Project
        {
            get
            {
                return project;
            }            
        }

        public ListEx<ProjectItem> Items
        {
            get { return items; }
        }

        public ProjectItem Parent
        {
            get { return parent; }
        }

        public int ProjectObjectID
        {
            get { return projectObjectID; }
            set { projectObjectID = value; }
        }

        public object ProjectObject
        {
            get { return projectObject; }
        }

        #endregion

        #region Methods

        public override string ToString()
        {
            return name;
        }

        #endregion

        #region Serialization

        internal void PutXml(XmlElement itemEl)
        {
            if (projectObject is Program)
            {
                projectObjectID = ((Program) projectObject).id;
            }
            
            Misc.CreateAppendXmlElement(itemEl, "name", name);
            Misc.CreateAppendXmlElement(itemEl, "obj_id", projectObjectID.ToString());
            Misc.CreateAppendXmlElement(itemEl, "type", ((int)type).ToString());
            Misc.CreateAppendXmlElement(itemEl, "data", System.Net.WebUtility.HtmlEncode(data));

            XmlElement itemsEl = Misc.CreateAppendXmlElement(itemEl, "items");

            foreach (ProjectItem pi in items)
            {
                XmlElement citemEl = Misc.CreateAppendXmlElement(itemsEl, "item");
                pi.PutXml(citemEl);
            }
        }

        internal void SetXml(XmlElement itemEl)
        {
            name = Misc.GetChildElementXml(itemEl, "name");
            projectObjectID = Conv.StringToInt(Misc.GetChildElementXml(itemEl, "obj_id"), projectObjectID);
            type = (ProjectItemType)Conv.StringToInt(Misc.GetChildElementXml(itemEl, "type"), (int)type);
            data = System.Net.WebUtility.HtmlDecode(Misc.GetChildElementXml(itemEl, "data", ""));

            if (type == ProjectItemType.Program)
            {
                Program prog = project.GetProgramByID(projectObjectID);
                projectObject = prog;
            }

            XmlElement itemsEl = itemEl["items"];
            foreach (XmlElement citemEl in itemsEl.ChildNodes)
            {
                ProjectItem pi = new ProjectItem();
                pi.project = project;
                pi.parent = this;
                pi.SetXml(citemEl);
                items.Add(pi);
            }
        }

        #endregion

    }

    #region ProjectItemEventHandler

    public class ProjectItemEventArgs
    {
        private ProjectItem projectItem;

        public ProjectItemEventArgs(ProjectItem projectItem)
        {
            this.projectItem = projectItem;
        }

        public ProjectItem Item
        {
            get { return projectItem; }
        }
    }

    public delegate void ProjectItemEventHandler(object sender, ProjectItemEventArgs e);

    #endregion

}
