using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.Specialized;

namespace digioz
{
    public class Setting
    {
        public const char DELIMITER = '|';

        public int Id
        {
            get { return Key.Id; }
        }

        public string Name
        {
            get { return csName.Count == 0 ? string.Empty : csName[csName.Count - 1]; }
            set {
                if (csName.Count == 0)
                {
                    csName.Add(value);
                }
                else
                {
                    csName[csName.Count - 1] = value;
                }
            }
        }

        public string FullName
        {
            get { return string.Join(DELIMITER.ToString(), csName.ToArray()); }
            protected internal set
            {
                csName.Clear();
                csName.AddRange(value.Split(new Char[] { DELIMITER }, StringSplitOptions.RemoveEmptyEntries));
            }
        }

        public bool Enabled
        {
            get { return cbEnabled; }
            set { cbEnabled = value; }
        }

        public Setting Parent
        {
            get { return coParent; }
            private set
            {
                if (value != coParent)
                {
                    if (coParent != null && coParent.Children.Contains(this))
                    {
                        coParent.Children.Remove(this);
                        Key.ParentId = 0;
                        FullName = Name;
                    }
                    coParent = (value != this ? value : null);
                    if (coParent != null)
                    {
                        coParent.Children.ListChanged -= new ListChangedEventHandler(OnParentChildrenChanged);
                        coParent.Children.ListChanged += new ListChangedEventHandler(OnParentChildrenChanged);
                        Key.ParentId = coParent.Key.Id;
                        FullName = coParent.FullName + DELIMITER + Name;
                    }
                }
            }
        }

        public SettingList Children
        {
            get { return coChildren; }
            private set { coChildren = value; }
        }

        public SettingKey Key
        {
            get { return coKey; }
            protected internal set { coKey = value; }
        }

        public Setting()
        {
            coKey = new SettingKey();
            csName = new List<string>();
            Enabled = true;
            Parent = null;
            Children = new SettingList();
            Children.ListChanged += new ListChangedEventHandler(OnChildrenChanged);
        }

        public Setting(string psName)
            : this()
        {
            Name = psName;
        }

        public Setting(string psName, bool pbEnabled) :
            this(psName)
        {
            Enabled = pbEnabled;
        }

        public Setting(int piId, int piParentId, string psName, bool pbEnabled)
            : this(psName, pbEnabled)
        {
            Key.Id = piId;
            Key.ParentId = piParentId;
        }

        protected internal Setting(int piId, int piParentId, string psFullName, string psName, bool pbEnabled)
            : this(piId, piParentId, psName, pbEnabled)
        {
            FullName = psFullName;
        }

        public override string ToString()
        {
            return FullName;
        }

        public override bool Equals(object obj)
        {
            return
                ReferenceEquals(this, obj) ||
                (
                    obj != null &&
                    obj is Setting &&
                    ((Setting)obj).FullName == FullName
                );
        }

        private void OnChildrenChanged(object sender, ListChangedEventArgs e)
        {
            switch (e.ListChangedType)
            {
                case ListChangedType.ItemAdded:
                    Children[e.NewIndex].Parent = this;
                    break;
            }
        }

        private void OnParentChildrenChanged(object sender, ListChangedEventArgs e)
        {
            switch (e.ListChangedType)
            {
                case ListChangedType.ItemDeleted:
                case ListChangedType.Reset:
                    if (!Parent.Children.Contains(this))
                    {
                        Parent.Children.ListChanged -= new ListChangedEventHandler(OnParentChildrenChanged);
                        Parent = null;
                    }
                    break;
            }
        }

        private SettingKey coKey;
        private List<string> csName;
        private bool cbEnabled;
        private Setting coParent;
        private SettingList coChildren;
    }

    public class Setting<tValue> : Setting
    {
        public tValue Value
        {
            get { return cgValue; }
            set { cgValue = value; }
        }

        public Setting()
            : base()
        {
            Value = default(tValue);
        }

        public Setting(string psName)
            : base(psName)
        {
            Value = default(tValue);
        }

        public Setting(string psName, tValue pgValue)
            : base(psName)
        {
            Value = pgValue;
        }

        public Setting(string psName, tValue pgValue, bool pbEnabled)
            : base(psName, pbEnabled)
        {
            Value = pgValue;
        }

        public Setting(ref Setting poSetting)
            : base(poSetting.Key.Id, poSetting.Key.ParentId, poSetting.Name, poSetting.Enabled)
        {
            Setting loParent = poSetting.Parent;
            if (loParent != null)
            {
                loParent.Children.Remove(poSetting);
                loParent.Children.Add(this);
            }
            Children.AddRange(poSetting.Children);
            poSetting = this;
        }

        public Setting(ref Setting poSetting, tValue pgValue)
            : this(ref poSetting)
        {
            Value = pgValue;
        }

        public Setting(int piId, int piParentId, string psName, tValue pgValue, bool pbEnabled)
            : base(piId, piParentId, psName, pbEnabled)
        {
            Value = pgValue;
        }

        protected internal Setting(int piId, int piParentId, string psFullName, string psName, tValue pgValue, bool pbEnabled)
            : base(piId, piParentId, psFullName, psName, pbEnabled)
        {
            Value = pgValue;
        }

        private tValue cgValue;
    }

    [Serializable]
    public class SettingKey
    {
        public int ParentId
        {
            get { return ciParentId; }
            protected internal set { ciParentId = value; }
        }

        public int Id
        {
            get { return ciId; }
            protected internal set { ciId = value; }
        }

        public SettingKey()
        {
            ciParentId = 0;
            ciId = 0;
        }

        public SettingKey(int piId)
            : this()
        {
            ciId = piId;
        }

        public SettingKey(int piParentId, int ciId) :
            this(ciId)
        {
            ciParentId = piParentId;
        }

        private int ciId;
        private int ciParentId;
    }

    public class SettingList : BindingList<Setting>
    {
        public SettingList() : base() { }

        public SettingList(SettingList<string> poSettingList)
        {
            if (poSettingList != null)
            {
                foreach (Setting loSetting in poSettingList)
                {
                    Add(loSetting);
                }
            }
        }

        public Setting this[string psFullName]
        {
            get { return this[IndexOf(new Setting(psFullName))]; }
            set { this[IndexOf(new Setting(psFullName))] = value; }
        }

        public void AddRange(IEnumerable<Setting> poSettings)
        {
            if (poSettings != null)
            {
                foreach (Setting loSetting in poSettings)
                {
                    Add(loSetting);
                }
            }
        }

        public void Remove(string psFullName)
        {
            RemoveAt(IndexOf(new Setting(psFullName)));
        }

        public bool Contains(string psFullName)
        {
            return Contains(new Setting(psFullName));
        }
    }

    public class SettingList<tValue> : BindingList<Setting<tValue>>
    {
        public Setting<tValue> this[string psFullName]
        {
            get
            {
                Setting loSetting = new Setting(psFullName);
                return this[IndexOf(new Setting<tValue>(ref loSetting))];
            }
            set
            {
                Setting loSetting = new Setting(psFullName);
                this[IndexOf(new Setting<tValue>(ref loSetting))] = value;
            }
        }

        public void AddRange(IEnumerable<Setting<tValue>> poSettings)
        {
            if (poSettings != null)
            {
                foreach (Setting<tValue> loSetting in poSettings)
                {
                    Add(loSetting);
                }
            }
        }

        public void Remove(string psFullName)
        {
            Setting loSetting = new Setting(psFullName);
            RemoveAt(IndexOf(new Setting<tValue>(ref loSetting)));
        }

        public bool Contains(string psFullName)
        {
            return Contains(new Setting<tValue>(psFullName));
        }
    }
}