using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace VisimBox.Scene
{
    /// <summary>
    /// Extended version of dictionary used to store children of the node
    /// </summary>
    public class NodeDictionary : IDictionary<string, Node>
    {
        protected Dictionary<string, Node> _dictionary = new Dictionary<string, Node>();
        protected Node _owner;
        protected Node _parent;

        /// <summary>
        /// No default constructor allowed
        /// </summary>
        protected NodeDictionary()
        {
        }

        /// <summary>
        /// Constructs Node Dictionary
        /// </summary>
        /// <param name="owner">Owner of the dictionary</param>
        /// <param name="parent">Parent of the owner</param>
        public NodeDictionary(Node owner, Node parent)
        {
            _owner = owner;
            _parent = parent;
        }

        /// <summary>
        /// Adds node to the dictionary. If node with this name already exists, node will be renamed!
        /// </summary>
        /// <param name="value">New node</param>
        public void Add(Node value)
        {
            Add(value.Name, value);
        }

        /// <summary>
        /// Returns number of children node starting with specified key
        /// </summary>
        /// <param name="key">name of node</param>
        /// <returns>Number of nodes</returns>
        public int ContainsKeys(string key)
        {
            Dictionary<string, Node>.KeyCollection keys = _dictionary.Keys;
            int rval = 0;
            foreach (string str in keys)
            {
                if (str.StartsWith(key))
                {
                    rval++;
                }
            }
            return rval;
        }

        /// <summary>
        /// Returns list of all nodes with names starting on key
        /// </summary>
        /// <param name="key">Name of the node</param>
        /// <returns>Null if no nodes, or list with nodes</returns>
        public List<Node> GetNodes(string key)
        {
            List<Node> rval = null;
            Dictionary<string, Node>.KeyCollection keys = _dictionary.Keys;
            foreach (string str in keys)
            {
                if (str.StartsWith(key))
                {
                    if (rval == null)
                        rval = new List<Node>();
                    rval.Add(this[str]);
                }
            }
            return rval;
        }

        /// <summary>
        /// Searches for child by fullname
        /// </summary>
        /// <param name="fullname">Fullname of the child</param>
        /// <returns>null or child node</returns>
        public Node GetChildByFullName(string fullname)
        {
            string ownname = _owner.Name;
            if (ownname == fullname)
                return _owner;

            string localname;

            if (ownname != null)
            {
                if (!fullname.StartsWith(ownname))
                    return null;
                if (fullname[ownname.Length] != '/')
                    return null;
                localname = fullname.Substring(ownname.Length + 1);
            }
            else
                localname = fullname.Substring(1);





            foreach (var val in _dictionary)
            {
                if (localname.StartsWith(val.Key))
                {
                    Node rval = val.Value.GetChildByFullName(localname);
                    if (rval != null)
                        return rval;
                }
            }
            return null;
        }


        /// <summary>
        /// Searches subnodes for specified name.
        /// </summary>
        /// <param name="name">Name of the desired child, can be specified with parent/parent2/child syntax to find correct node</param>
        /// <returns>Null or found child node</returns>
        public Node SearchChild(string name)
        {
            if (name[name.Length - 1] == '/')
                name = name.Substring(0, name.Length - 1);

            string ownname = _owner.Name;
            if (ownname == name)
                return _owner;

            int idx = name.IndexOf('/');
            if (idx != -1)
            {
                if (name.Substring(0, idx) == ownname)
                {
                    //required name starts with the owner name - so try to locate the rest among children

                    string newname = name.Substring(idx + 1);
                    foreach (var val in _dictionary)
                    {
                        Node rval = val.Value.SearchChild(newname);
                        if (rval != null)
                            return rval;
                    }
                }
            }

            foreach (var val in _dictionary)
            {
                Node rval = val.Value.SearchChild(name);
                if (rval != null)
                    return rval;
            }

            return null;
        }


        #region IDictionary<string,Node> Members

        /// <summary>
        /// Adds new node to the list. If key already exists, it will be changed to keyNumber, ie key0, key1, key2 etc. Node will be also renamed.
        /// Key cannot contain character "/" - it will be automatically replaced to -.
        /// </summary>
        /// <param name="key">required key</param>
        /// <param name="value">required value</param>
        public void Add(string key, Node value)
        {
            string newname = key;
            if (newname.IndexOf('/') >= 0)
                newname = newname.Replace('/', '-');

            
            if (_dictionary.ContainsKey(key))                        
            {
                //key adding failed, so there's already some child. Search for next possible name

                Dictionary<string, Node>.KeyCollection keys = _dictionary.Keys;

                int index = 0;

                foreach (string str in keys)
                {
                    if (str.StartsWith(key) && str.Length > key.Length)
                    {
                        string subs = str.Substring(key.Length);
                        int i;
                        if (int.TryParse(subs, out i))
                        {
                            if (i >= index)
                                index = i + 1;
                        }
                    }
                }

                newname = string.Format("{0}{1}", key, index);
                _dictionary.Add(newname, value);
            }
            else _dictionary.Add(key, value);
            
            value.RenameForDictionary(newname);

            value.ChangeParent(this._owner, true);
        }

        public bool ContainsKey(string key)
        {
            return _dictionary.ContainsKey(key);
        }

        public ICollection<string> Keys
        {
            get { return _dictionary.Keys; }
        }

        /// <summary>
        /// Removes node from the dictionary
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Remove(string key)
        {
            Node v;
            if (!TryGetValue(key, out v))
                return false;
            if (!_dictionary.Remove(key))
                return false;
            v.ChangeParent(null, false);
            return true;
        }

        public bool Remove(Node node)
        {
            if (!_dictionary.Remove(node.Name))
                return false;
            node.ChangeParent(null, false);
            return true;
        }

        public void RenameChild(Node child, string newname)
        {
            //sanity check
        
            if (ContainsKey(child.Name) && (this[child.Name] == child))
            {
                Remove(child.Name);
                Add(newname, child);
            }
            else
                Console.DebugLine("Attempt to rename node " + child.FullName + ", but node parent " + _owner.FullName + " doesn't have it!");
        }



        public bool TryGetValue(string key, out Node value)
        {
            return _dictionary.TryGetValue(key, out value);
        }

        public ICollection<Node> Values
        {
            get { return _dictionary.Values; }
        }

        public Node this[string key]
        {
            get
            {
                return _dictionary[key];
            }
            set
            {
                _dictionary[key] = value;
            }
        }

        #endregion

        #region ICollection<KeyValuePair<string,Node>> Members

        public void Add(KeyValuePair<string, Node> item)
        {
            Add(item.Key, item.Value);
        }

        public void Clear()
        {
            foreach (var kv in _dictionary)
            {
                kv.Value.Dispose();
            }
            _dictionary.Clear();
        }

        public bool Contains(KeyValuePair<string, Node> item)
        {
            return _dictionary.Contains(item);
        }

        public void CopyTo(KeyValuePair<string, Node>[] array, int arrayIndex)
        {
            ICollection<KeyValuePair<string, Node>> icol = _dictionary as ICollection<KeyValuePair<string, Node>>;
            icol.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _dictionary.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(KeyValuePair<string, Node> item)
        {
            return Remove(item.Key);
        }

        #endregion

        #region IEnumerable<KeyValuePair<string,Node>> Members

        public IEnumerator<KeyValuePair<string, Node>> GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            System.Collections.IEnumerable iem = _dictionary as System.Collections.IEnumerable;
            return iem.GetEnumerator();
        }

        #endregion
    }
}
