using System;
using System.Collections.Generic;
using System.Text;
using nft.framework.loader;

namespace nft.framework
{
    public class ParamsReader
    {
        private string sourceUri;
        private IParamsParser[] loader;
        protected IDictionary<string, ParamsReader> overwrites = null;

        public ParamsReader(string srcUri, IParamsParser ploader) {
            this.sourceUri = srcUri;
            Loaders = ploader != null ? new IParamsParser[] { ploader } : null;
        }

        protected ParamsReader(string srcUri, IParamsParser[] ploaders) {
            this.sourceUri = srcUri;
            Loaders = ploaders;
        }

        public string InnerText {
            get { return PrimaryLoader.InnerText; }
        }

        /// <summary>
        /// Returns InnerText if not null. Returns default_val if InnerText is null.
        /// </summary>
        /// <param name="defalut_val"></param>
        /// <returns></returns>
        public string InnerTextOr(string defalut_val) {
            string ret =PrimaryLoader.InnerText;
            return (ret != null) ? ret : defalut_val;
        }

        /// <summary>
        /// Returns child element corresponds to the key.
        /// If this reader contains multiple value, this method enumelates for the first element.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public ParamsReader this[string key] {
            get {
                if (key == null || key.Length == 0) {
                    return new ParamsReader(sourceUri, NullParam.TheInstance);
                }
                String[] keys = key.Split('|');
                if (keys.Length == 1) {
                    IParamsParser[] parr = FindForKey(PrimaryLoader, keys[0]);
                    return new ParamsReader(sourceUri, parr);
                } else {
                    IParamsParser[] parr = FindForMultiKeys(PrimaryLoader, keys);
                    return new ParamsReader(sourceUri, parr);
                }
            }
        }

        public void OverWrite(string key, string value) {
            OverWrite(key, new ParamsReader(sourceUri, new PlainStringParam(value)));
        }

        public void OverWrite(string key, ParamsReader value) {
            if (overwrites == null) {
                overwrites = new Dictionary<string, ParamsReader>();
            }
            overwrites.Add(key, value);
        }

        public IFileSource GetFileSource(string path) {
            IFileSource src = null;
            foreach (IParamsParser pp in loader) {
                src = pp.GetFileSource(path);
                if (src != null) break;
            }
            return src;
        }

        public ParamsReader this[int index] {
            get {
                if (index < 0 || index >= loader.Length) {
                    return new ParamsReader(sourceUri, NullParam.TheInstance);
                } else {
                    return new ParamsReader(sourceUri, loader[index]);
                }
            }
        }

        public string SourceURI { get { return sourceUri; } }

        public bool IsNull {
            get { return loader.Length == 0 || PrimaryLoader.IsNull; }
        }
        /*
        public bool IsMap {
            get { return loader.IsMap; }
        }
        */
        /// <summary>
        /// True means multiple values.  (Result of multiple children for some key)
        /// </summary>
        public bool IsArray {
            get { return loader.Length>1; }
        }
        /// <summary>
        /// Returns count of elements. (Result of multiple children for some key)
        /// </summary>
        public int Length {
            get { return loader.Length; }
        }

        /// <summary>
        /// Enumelate all children
        /// </summary>
        public IEnumerable<ParamsReader> Each {
            get {
                return new ParamsArrayEnumerator(sourceUri, loader);
            }
        }

        /// <summary>
        /// Enumelate all children witch match specified key.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IEnumerable<ParamsReader> EnumChildren(string key) {
            return new KeySpecifiedChildParamsEnumerator(sourceUri, loader, key);
        }

        /// <summary>
        /// Enumelate all children with it's key.
        /// If this reader contains multiple value, this method enumelates for the first element.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<KeyValuePair<string,ParamsReader>> EnumChildren() {
            return new ChildParamsReaderEnumerator(sourceUri, PrimaryLoader);
        }

        protected IParamsParser PrimaryLoader {
            get {
                return loader.Length>0 ? loader[0]:NullParam.TheInstance;
            }
        }

        protected IParamsParser[] Loaders {
            get {
                return loader;
            }
            set {
                if (value != null) {
                    loader = value;
                } else {
                    loader = new IParamsParser[0];
                }
            }
        }

        protected IParamsParser[] FindForKey(IParamsParser p, String key) {
            List<IParamsParser> arr = new List<IParamsParser>();
            ParamsReader ror;
            // append overwirted value at first (if exist).
            if (overwrites != null && overwrites.TryGetValue(key, out ror)) {
                arr.Add(ror.PrimaryLoader);
            }
            IEnumerator<IParamsParser> en = p.EnumChildren(key);
            while (en.MoveNext()) {
                arr.Add(en.Current);
            }
            return arr.ToArray();
        }

        protected IParamsParser[] FindForMultiKeys(IParamsParser p, String[] keys) {
            List<IParamsParser> arr = new List<IParamsParser>();
            foreach (String k in keys) {
                ParamsReader ror;
                // append overwirted value at first (if exist).
                if (overwrites != null && overwrites.TryGetValue(k, out ror)) {
                    arr.Add(ror.PrimaryLoader);
                    break;
                }
            }
            IEnumerator<KeyValuePair<String, IParamsParser>> en = p.EnumChildren();
            while (en.MoveNext()) {
                foreach (String k in keys) {
                    KeyValuePair<String, IParamsParser> pair = en.Current;
                    if (pair.Key.Equals(k)) {
                        arr.Add(pair.Value);
                        break;
                    }
                }
            }
            return arr.ToArray();
        }

        internal protected class ParamsArrayEnumerator : IEnumerable<ParamsReader>
        {
            protected readonly IParamsParser[] array;
            protected readonly string sourceUri;
            public ParamsArrayEnumerator(string srcUri, IParamsParser[] arr) {
                this.sourceUri = srcUri;
                if (arr == null) arr = new IParamsParser[0];
                this.array = arr;
            }

            public IEnumerator<ParamsReader> GetEnumerator() {
                foreach (IParamsParser pp in array) {
                    yield return new ParamsReader(sourceUri, pp);
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }

        internal protected class KeySpecifiedChildParamsEnumerator : IEnumerable<ParamsReader>
        {
            protected readonly IParamsParser[] array;
            protected readonly string sourceUri;
            protected readonly string key;

            public KeySpecifiedChildParamsEnumerator(string srcUri, IParamsParser[] arr, string key) {
                this.sourceUri = srcUri;
                if (arr == null) arr = new IParamsParser[0];
                this.array = arr;
                this.key = key;
            }

            public IEnumerator<ParamsReader> GetEnumerator() {
                if(key==null) yield break;
                String[] keys = key.Split('|');
                if (keys.Length == 1) {
                    foreach (IParamsParser pp in array) {
                        IEnumerator<IParamsParser> en = pp.EnumChildren(key);
                        while (en.MoveNext()) {
                            yield return new ParamsReader(sourceUri, en.Current);
                        }
                    }
                } else {
                    foreach (IParamsParser pp in array) {
                        IEnumerator<KeyValuePair<String, IParamsParser>> en = pp.EnumChildren();
                        while (en.MoveNext()) {
                            foreach (String k in keys) {
                                if (en.Current.Key.Equals(keys)) {
                                    yield return new ParamsReader(sourceUri, en.Current.Value);
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }

        internal protected class ChildParamsReaderEnumerator : IEnumerable<KeyValuePair<string, ParamsReader>>
        {
            protected readonly IParamsParser parser;
            protected readonly string sourceUri;
            public ChildParamsReaderEnumerator(string srcUri, IParamsParser pp) {
                this.sourceUri = srcUri;
                if (pp == null) pp = NullParam.TheInstance;
                this.parser = pp;
            }

            public IEnumerator<KeyValuePair<string, ParamsReader>> GetEnumerator() {
                IEnumerator<KeyValuePair<string, IParamsParser>> en = parser.EnumChildren();
                while (en.MoveNext()) {
                    KeyValuePair<string, IParamsParser> pair = en.Current;
                    ParamsReader pr = new ParamsReader(sourceUri, pair.Value);
                    yield return new KeyValuePair<string, ParamsReader>(pair.Key, pr);
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }
    }
}
