/*******************************************************************************
 *  Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License"). You may not use
 *  this file except in compliance with the License. A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 *  or in the "license" file accompanying this file.
 *  This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *  CONDITIONS OF ANY KIND, either express or implied. See the License for the
 *  specific language governing permissions and limitations under the License.
 * *****************************************************************************
 *    __  _    _  ___
 *   (  )( \/\/ )/ __)
 *   /__\ \    / \__ \
 *  (_)(_) \/\/  (___/
 *
 *  AWS SDK for .NET
 *
 */
using System;
using System.ComponentModel;
using System.Globalization;
using Amazon.Util;
using Amazon.Util.Internal;
using System.Collections.Generic;
using Amazon.Runtime;
namespace Amazon
{
    /// 
    /// Configuration options that apply to the entire SDK.
    /// 
    /// These settings can be configured through app.config or web.config.
    /// Below is a full sample configuration that illustrates all the possible options.
    /// 
    /// <configSections>
    ///   <section name="aws" type="Amazon.AWSSection, AWSSDK.Core"/>
    /// </configSections>
    /// <aws region="us-west-2">
    ///   <logging logTo="Log4Net, SystemDiagnostics" logResponses="Always" logMetrics="true" />
    ///   <s3 useSignatureVersion4="true" />
    ///   <proxy host="localhost" port="8888" username="1" password="1" />
    ///   
    ///   <dynamoDB>
    ///     <dynamoDBContext tableNamePrefix="Prod-">
    /// 
    ///       <tableAliases>
    ///         <alias fromTable="FakeTable" toTable="People" />
    ///         <alias fromTable="Persons" toTable="People" />
    ///       </tableAliases>
    /// 
    ///       <mappings>
    ///         <map type="Sample.Tests.Author, SampleDLL" targetTable="People" />
    ///         <map type="Sample.Tests.Editor, SampleDLL" targetTable="People">
    ///           <property name="FullName" attribute="Name" />
    ///           <property name="EmployeeId" attribute="Id" />
    ///           <property name="ComplexData" converter="Sample.Tests.ComplexDataConverter, SampleDLL" />
    ///           <property name="Version" version="true" />
    ///           <property name="Password" ignore="true" />
    ///         </map>
    ///       </mappings>
    /// 
    ///     </dynamoDBContext>
    ///   </dynamoDB>
    /// </aws>
    /// 
    /// 
    public static partial class AWSConfigs
    {
        #region Private static members
        private static char[] validSeparators = new char[] { ' ', ',' };
        // Tests can override this DateTime source.
        internal static Func utcNowSource = GetUtcNow;
        // Deprecated configs
        internal static string _awsRegion = GetConfig(AWSRegionKey);
        internal static LoggingOptions _logging = GetLoggingSetting();
        internal static ResponseLoggingOption _responseLogging = GetConfigEnum(ResponseLoggingKey);
        internal static bool _logMetrics = GetConfigBool(LogMetricsKey);
        internal static string _endpointDefinition = GetConfig(EndpointDefinitionKey);
        internal static string _awsProfileName = GetConfig(AWSProfileNameKey);
        internal static string _awsAccountsLocation = GetConfig(AWSProfilesLocationKey);
        internal static bool _useSdkCache = GetConfigBool(UseSdkCacheKey, defaultValue: true);
        // for reading from awsconfigs.xml
        private static object _lock = new object();
        private static List standardConfigs = new List() { "region", "logging", "correctForClockSkew" };
#pragma warning disable 414
        private static bool configPresent = true;
#pragma warning restore 414
        // New config section
        private static RootConfig _rootConfig = new RootConfig();
        #endregion
        #region Clock Skew
        /// 
        /// Manual offset to apply to client clock.  This is a global setting that overrides 
        /// ClockOffset value calculated for all service endpoints.
        /// 
        public static TimeSpan? ManualClockCorrection
        {
            get
            {
                return Runtime.CorrectClockSkew.GlobalClockCorrection;
            }
            set
            {
                Runtime.CorrectClockSkew.GlobalClockCorrection = value;
            }
        }
        /// 
        /// Determines if the SDK should correct for client clock skew
        /// by determining the correct server time and reissuing the
        /// request with the correct time.
        /// Default value of this field is True.
        ///  will be updated with the calculated
        /// offset even if this field is set to false, though requests
        /// will not be corrected or retried.
        /// Ignored if  is set.
        /// 
        public static bool CorrectForClockSkew
        {
            get { return _rootConfig.CorrectForClockSkew; }
            set { _rootConfig.CorrectForClockSkew = value; }
        }
        /// 
        /// The calculated clock skew correction, if there is one.
        /// This field will be set if a service call resulted in an exception
        /// and the SDK has determined that there is a difference between local
        /// and server times.
        /// 
        /// If  is set to true, this
        /// value will be set to the correction, but it will not be used by the
        /// SDK and clock skew errors will not be retried.
        /// 
        [Obsolete("This value is deprecated in favor of IClientConfig.ClockOffset")]
        public static TimeSpan ClockOffset
        {
            get;
            internal set;
        }
        #endregion
        #region Region
        /// 
        /// Key for the AWSRegion property.
        /// 
        /// 
        public const string AWSRegionKey = "AWSRegion";
        /// 
        /// Configures the default AWS region for clients which have not explicitly specified a region.
        /// Changes to this setting will only take effect for newly constructed instances of AWS clients.
        /// 
        /// This setting can be configured through the App.config. For example:
        /// 
        /// <configSections>
        ///   <section name="aws" type="Amazon.AWSSection, AWSSDK.Core"/>
        /// </configSections>
        /// <aws region="us-west-2" />
        /// 
        /// 
        public static string AWSRegion
        {
            get { return _rootConfig.Region; }
            set { _rootConfig.Region = value; }
        }
        #endregion
        #region Account Name
        /// 
        /// Key for the AWSProfileName property.
        /// 
        /// 
        public const string AWSProfileNameKey = "AWSProfileName";
        /// 
        /// Profile name for stored AWS credentials that will be used to make service calls.
        /// Changes to this setting will only take effect in newly-constructed clients.
        /// 
        /// To reference the account from an application's App.config or Web.config use the AWSProfileName setting.
        /// 
        /// <?xml version="1.0" encoding="utf-8" ?>
        /// <configuration>
        ///     <appSettings>
        ///         <add key="AWSProfileName" value="development"/>
        ///     </appSettings>
        /// </configuration>
        /// 
        /// 
        /// 
        public static string AWSProfileName
        {
            get { return _rootConfig.ProfileName; }
            set { _rootConfig.ProfileName = value; }
        }
        #endregion
        #region Accounts Location
        /// 
        /// Key for the AWSProfilesLocation property.
        /// 
        /// 
        public const string AWSProfilesLocationKey = "AWSProfilesLocation";
        /// 
        /// Location of the credentials file shared with other AWS SDKs.
        /// By default, the credentials file is stored in the .aws directory in the current user's home directory.
        /// 
        /// Changes to this setting will only take effect in newly-constructed clients.
        /// 
        /// To reference the profile from an application's App.config or Web.config use the AWSProfileName setting.
        /// 
        /// <?xml version="1.0" encoding="utf-8" ?>
        /// <configuration>
        ///     <appSettings>
        ///         <add key="AWSProfilesLocation" value="c:\config"/>
        ///     </appSettings>
        /// </configuration>
        /// 
        /// 
        /// 
        public static string AWSProfilesLocation
        {
            get { return _rootConfig.ProfilesLocation; }
            set { _rootConfig.ProfilesLocation = value; }
        }
        #endregion
        #region Logging
        /// 
        /// Key for the Logging property.
        /// 
        /// 
        public const string LoggingKey = "AWSLogging";
        /// 
        /// Configures how the SDK should log events, if at all.
        /// Changes to this setting will only take effect in newly-constructed clients.
        /// 
        /// The setting can be configured through App.config, for example:
        /// 
        /// <appSettings>
        ///   <add key="AWSLogging" value="log4net"/>
        /// </appSettings>
        /// 
        /// 
        [Obsolete("This property is obsolete. Use LoggingConfig.LogTo instead.")]
        public static LoggingOptions Logging
        {
            get { return _rootConfig.Logging.LogTo; }
            set { _rootConfig.Logging.LogTo = value; }
        }
        private static LoggingOptions GetLoggingSetting()
        {
            string value = GetConfig(LoggingKey);
            if (string.IsNullOrEmpty(value))
                return LoggingOptions.None;
            string[] settings = value.Split(validSeparators, StringSplitOptions.RemoveEmptyEntries);
            if (settings == null || settings.Length == 0)
                return LoggingOptions.None;
            LoggingOptions totalSetting = LoggingOptions.None;
            foreach (string setting in settings)
            {
                LoggingOptions l = ParseEnum(setting);
                totalSetting |= l;
            }
            return totalSetting;
        }
        #endregion
        #region Response Logging
        /// 
        /// Key for the ResponseLogging property.
        /// 
        /// 
        /// 
        public const string ResponseLoggingKey = "AWSResponseLogging";
        /// 
        /// Configures when the SDK should log service responses.
        /// Changes to this setting will take effect immediately.
        /// 
        /// The setting can be configured through App.config, for example:
        /// 
        /// <appSettings>
        ///   <add key="AWSResponseLogging" value="OnError"/>
        /// </appSettings>
        /// 
        /// 
        [Obsolete("This property is obsolete. Use LoggingConfig.LogResponses instead.")]
        public static ResponseLoggingOption ResponseLogging
        {
            get { return _rootConfig.Logging.LogResponses; }
            set { _rootConfig.Logging.LogResponses = value; }
        }
        #endregion
        #region Log Metrics
        /// 
        /// Key for the LogMetrics property.
        /// 
        /// 
        public const string LogMetricsKey = "AWSLogMetrics";
        /// 
        /// Configures if the SDK should log performance metrics.
        /// This setting configures the default LogMetrics property for all clients/configs.
        /// Changes to this setting will only take effect in newly-constructed clients.
        /// 
        /// The setting can be configured through App.config, for example:
        /// 
        /// <appSettings>
        ///   <add key="AWSLogMetrics" value="true"/>
        /// </appSettings>
        /// 
        /// 
        [Obsolete("This property is obsolete. Use LoggingConfig.LogMetrics instead.")]
        public static bool LogMetrics
        {
            get { return _rootConfig.Logging.LogMetrics; }
            set { _rootConfig.Logging.LogMetrics = value; }
        }
        #endregion
        #region Endpoint Configuration
        /// 
        /// Key for the EndpointDefinition property.
        /// 
        /// 
        public const string EndpointDefinitionKey = "AWSEndpointDefinition";
        /// 
        /// Configures if the SDK should use a custom configuration file that defines the regions and endpoints.
        /// 
        /// <configSections>
        ///   <section name="aws" type="Amazon.AWSSection, AWSSDK.Core"/>
        /// </configSections>
        /// <aws endpointDefinition="c:\config\endpoints.json" />
        /// 
        /// 
        public static string EndpointDefinition
        {
            get { return _rootConfig.EndpointDefinition; }
            set { _rootConfig.EndpointDefinition = value; }
        }
        #endregion
        #region SDK Cache
        /// 
        /// Key for the UseSdkCache property.
        /// 
        /// 
        public const string UseSdkCacheKey = "AWSCache";
        /// 
        /// Configures if the SDK Cache should be used, the default value is true.
        /// 
        /// <configSections>
        ///   <section name="aws" type="Amazon.AWSSection, AWSSDK.Core"/>
        /// </configSections>
        /// <aws useSdkCache="true" />
        /// 
        /// 
        public static bool UseSdkCache
        {
            get { return _rootConfig.UseSdkCache; }
            set { _rootConfig.UseSdkCache = value; }
        }
        #endregion
        #region AWS Config Sections
        /// 
        /// Configuration for the Logging section of AWS configuration.
        /// Changes to some settings may not take effect until a new client is constructed.
        /// 
        /// Example section:
        /// 
        /// <configSections>
        ///   <section name="aws" type="Amazon.AWSSection, AWSSDK.Core"/>
        /// </configSections>
        /// <aws>
        ///   <logging logTo="Log4Net, SystemDiagnostics" logResponses="Always" logMetrics="true" />
        /// </aws>
        /// 
        /// 
        public static LoggingConfig LoggingConfig { get { return _rootConfig.Logging; } }
        /// 
        /// Configuration for the Proxy section of AWS configuration.
        /// Changes to some settings may not take effect until a new client is constructed.
        /// 
        /// Example section:
        /// 
        /// <configSections>
        ///   <section name="aws" type="Amazon.AWSSection, AWSSDK.Core"/>
        /// </configSections>
        /// <aws>
        ///   <proxy host="localhost" port="8888" username="1" password="1" bypassList="addressexpr1;addressexpr2;..." bypassOnLocal="true" />
        /// </aws>
        /// 
        /// 
        public static ProxyConfig ProxyConfig { get { return _rootConfig.Proxy; } }
        /// 
        /// When set to true, the service client will use the  x-amz-user-agent
        /// header instead of the User-Agent header to report version and
        /// environment information to the AWS service.
        ///
        /// Note: This is especially useful when using a platform like WebAssembly
        /// which doesn't allow to specify the User-Agent header.
        /// 
        public static bool UseAlternateUserAgentHeader
        {
            get { return _rootConfig.UseAlternateUserAgentHeader; }
            set { _rootConfig.UseAlternateUserAgentHeader = value; }
        }
        /// 
        /// Configuration for the region endpoint section of AWS configuration.
        /// Changes may not take effect until a new client is constructed.
        /// 
        /// Example section:
        /// 
        /// <configSections>
        ///   <section name="aws" type="Amazon.AWSSection, AWSSDK.Core"/>
        /// </configSections>
        /// <aws region="us-west-2" />
        /// 
        /// 
        public static RegionEndpoint RegionEndpoint
        {
            get { return _rootConfig.RegionEndpoint; }
            set { _rootConfig.RegionEndpoint = value; }
        }
        public static CSMConfig CSMConfig
        {
            get { return _rootConfig.CSMConfig; }
            set { _rootConfig.CSMConfig = value; }
        }
        #endregion
        #region Internal members
        internal const string LoggingDestinationProperty = "LogTo";
        internal static PropertyChangedEventHandler mPropertyChanged;
        /// 
        /// Lock for SomeEvent delegate access.
        /// 
        internal static readonly object propertyChangedLock = new object();
        internal static event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                lock (propertyChangedLock)
                {
                    mPropertyChanged += value;
                }
            }
            remove
            {
                lock (propertyChangedLock)
                {
                    mPropertyChanged -= value;
                }
            }
        }
        internal static void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = mPropertyChanged;
            if (handler != null)
            {
                handler(null, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
        #region Private general methods
        private static bool GetConfigBool(string name, bool defaultValue = false)
        {
            string value = GetConfig(name);
            bool result;
            if (bool.TryParse(value, out result))
                return result;
            return defaultValue;
        }
        private static T GetConfigEnum(string name)
        {
            var type = TypeFactory.GetTypeInfo(typeof(T));
            if (!type.IsEnum) throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type {0} must be enum", type.FullName));
            string value = GetConfig(name);
            if (string.IsNullOrEmpty(value))
                return default(T);
            T result = ParseEnum(value);
            return result;
        }
        private static T ParseEnum(string value)
        {
            T t;
            if (TryParseEnum(value, out t))
                return t;
            Type type = typeof(T);
            string messageFormat = "Unable to parse value {0} as enum of type {1}. Valid values are: {2}";
            string enumNames = string.Join(", ", Enum.GetNames(type));
            throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, messageFormat, value, type.FullName, enumNames));
        }
        private static bool TryParseEnum(string value, out T result)
        {
            result = default(T);
            if (string.IsNullOrEmpty(value))
                return false;
            try
            {
                T t = (T)Enum.Parse(typeof(T), value, true);
                result = t;
                return true;
            }
            catch (ArgumentException)
            {
                return false;
            }
        }
        /// 
        /// This method should never be called directly.
        /// Call AWSSDKUtils.CorrectedUtcNow instead.
        /// 
        /// 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("AwsSdkRules", "CR1003:PreventDateTimeNowUseRule")]
        private static DateTime GetUtcNow()
        {
            return DateTime.UtcNow;
        }
        #endregion
    }
    /// 
    /// Logging options.
    /// Can be combined to enable multiple loggers.
    /// 
    [Flags]
    public enum LoggingOptions
    {
        /// 
        /// No logging
        /// 
        None = 0,
        /// 
        /// Log using log4net
        /// 
        Log4Net = 1,
        /// 
        /// Log using System.Diagnostics
        /// 
        SystemDiagnostics = 2,
        /// 
        /// Log to the console
        /// 
        Console = 16
    }
    /// 
    /// Response logging option.
    /// 
    public enum ResponseLoggingOption
    {
        /// 
        /// Never log service response
        /// 
        Never = 0,
        /// 
        /// Only log service response when there's an error
        /// 
        OnError = 1,
        /// 
        /// Always log service response
        /// 
        Always = 2
    }
    /// 
    /// Format for metrics data in the logs
    /// 
    public enum LogMetricsFormatOption
    {
        /// 
        /// Emit metrics in human-readable format
        /// 
        Standard = 0,
        /// 
        /// Emit metrics as JSON data
        /// 
        JSON = 1
    }
}