// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections;
using System.Collections.Specialized;

namespace System.Configuration
{
    internal class ConfigurationValues : NameObjectCollectionBase
    {
        private static volatile IEnumerable s_emptyCollection;
        private BaseConfigurationRecord _configRecord;
        private volatile bool _containsElement;
        private volatile bool _containsInvalidValue;

        internal ConfigurationValues() : base(StringComparer.Ordinal) { }

        internal object this[string key]
        {
            get
            {
                ConfigurationValue configValue = GetConfigValue(key);
                return configValue?.Value;
            }
            set { SetValue(key, value, ConfigurationValueFlags.Modified, null); }
        }

        internal object this[int index]
        {
            get
            {
                ConfigurationValue configValue = GetConfigValue(index);
                return configValue?.Value;
            }
        }

        internal object SyncRoot => this;

        internal IEnumerable ConfigurationElements
            => _containsElement ? new ConfigurationElementsCollection(this) : EmptyCollectionInstance;

        internal IEnumerable InvalidValues
            => _containsInvalidValue ? new InvalidValuesCollection(this) : EmptyCollectionInstance;

        private static IEnumerable EmptyCollectionInstance
            => s_emptyCollection ?? (s_emptyCollection = new EmptyCollection());

        internal void AssociateContext(BaseConfigurationRecord configRecord)
        {
            _configRecord = configRecord;

            // Associate with children
            foreach (ConfigurationElement currentElement in ConfigurationElements)
                currentElement.AssociateContext(_configRecord);
        }

        internal bool Contains(string key)
        {
            return BaseGet(key) != null;
        }

        internal string GetKey(int index)
        {
            return BaseGetKey(index);
        }

        internal ConfigurationValue GetConfigValue(string key)
        {
            return (ConfigurationValue)BaseGet(key);
        }

        internal ConfigurationValue GetConfigValue(int index)
        {
            return (ConfigurationValue)BaseGet(index);
        }

        internal PropertySourceInfo GetSourceInfo(string key)
        {
            ConfigurationValue configurationValue = GetConfigValue(key);
            return configurationValue?.SourceInfo;
        }

        internal void ChangeSourceInfo(string key, PropertySourceInfo sourceInfo)
        {
            ConfigurationValue configurationValue = GetConfigValue(key);
            if (configurationValue != null) configurationValue.SourceInfo = sourceInfo;
        }

        private ConfigurationValue CreateConfigValue(object value, ConfigurationValueFlags valueFlags,
            PropertySourceInfo sourceInfo)
        {
            if (value != null)
            {
                if (value is ConfigurationElement)
                {
                    _containsElement = true;
                    ((ConfigurationElement)value).AssociateContext(_configRecord);
                }
                else
                {
                    if (value is InvalidPropValue) _containsInvalidValue = true;
                }
            }

            ConfigurationValue configValue = new ConfigurationValue(value, valueFlags, sourceInfo);
            return configValue;
        }

        internal void SetValue(string key, object value, ConfigurationValueFlags valueFlags,
            PropertySourceInfo sourceInfo)
        {
            ConfigurationValue configValue = CreateConfigValue(value, valueFlags, sourceInfo);
            BaseSet(key, configValue);
        }

        internal void Clear()
        {
            BaseClear();
        }

        internal ConfigurationValueFlags RetrieveFlags(string key)
        {
            ConfigurationValue configurationValue = (ConfigurationValue)BaseGet(key);
            return configurationValue?.ValueFlags ?? ConfigurationValueFlags.Default;
        }

        internal bool IsModified(string key)
        {
            ConfigurationValue configurationValue = (ConfigurationValue)BaseGet(key);
            if (configurationValue != null)
                return (configurationValue.ValueFlags & ConfigurationValueFlags.Modified) != 0;
            return false;
        }

        internal bool IsInherited(string key)
        {
            ConfigurationValue configurationValue = (ConfigurationValue)BaseGet(key);
            if (configurationValue != null)
                return (configurationValue.ValueFlags & ConfigurationValueFlags.Inherited) != 0;
            return false;
        }

        private class EmptyCollection : IEnumerable
        {
            private readonly IEnumerator _emptyEnumerator;

            internal EmptyCollection()
            {
                _emptyEnumerator = new EmptyCollectionEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return _emptyEnumerator;
            }

            private class EmptyCollectionEnumerator : IEnumerator
            {
                bool IEnumerator.MoveNext()
                {
                    return false;
                }

                object IEnumerator.Current => null;

                void IEnumerator.Reset() { }
            }
        }

        private class ConfigurationElementsCollection : IEnumerable
        {
            private readonly ConfigurationValues _values;

            internal ConfigurationElementsCollection(ConfigurationValues values)
            {
                _values = values;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                if (_values._containsElement)
                {
                    for (int index = 0; index < _values.Count; index++)
                    {
                        object value = _values[index];
                        if (value is ConfigurationElement) yield return value;
                    }
                }
            }
        }

        private class InvalidValuesCollection : IEnumerable
        {
            private readonly ConfigurationValues _values;

            internal InvalidValuesCollection(ConfigurationValues values)
            {
                _values = values;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                if (_values._containsInvalidValue)
                {
                    for (int index = 0; index < _values.Count; index++)
                    {
                        object value = _values[index];
                        if (value is InvalidPropValue) yield return value;
                    }
                }
            }
        }
    }
}