// 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.

//
//
// Description: Helper class for miscellaneous framework-level features related
// to DependencyProperties.
//
// See spec at GetValueSource.mht
//

using System;
using MS.Internal;

namespace System.Windows
{
    /// <summary>
    /// Source of a DependencyProperty value.
    /// </summary>
    public enum BaseValueSource
    {
        /// <summary> The source is not known by the Framework. </summary>
        Unknown                 = BaseValueSourceInternal.Unknown,
        /// <summary> Default value, as defined by property metadata. </summary>
        Default                 = BaseValueSourceInternal.Default,
        /// <summary> Inherited from an ancestor. </summary>
        Inherited               = BaseValueSourceInternal.Inherited,
        /// <summary> Default Style for the current theme. </summary>
        DefaultStyle            = BaseValueSourceInternal.ThemeStyle,
        /// <summary> Trigger in the default Style for the current theme. </summary>
        DefaultStyleTrigger     = BaseValueSourceInternal.ThemeStyleTrigger,
        /// <summary> Style setter. </summary>
        Style                   = BaseValueSourceInternal.Style,
        /// <summary> Trigger in the Template. </summary>
        TemplateTrigger         = BaseValueSourceInternal.TemplateTrigger,
        /// <summary> Trigger in the Style. </summary>
        StyleTrigger            = BaseValueSourceInternal.StyleTrigger,
        /// <summary> Implicit Style reference. </summary>
        ImplicitStyleReference  = BaseValueSourceInternal.ImplicitReference,
        /// <summary> Template that created the element. </summary>
        ParentTemplate          = BaseValueSourceInternal.ParentTemplate,
        /// <summary> Trigger in the Template that created the element. </summary>
        ParentTemplateTrigger   = BaseValueSourceInternal.ParentTemplateTrigger,
        /// <summary> Local value. </summary>
        Local                   = BaseValueSourceInternal.Local,
    }

    /// <summary>
    /// This struct contains the information returned from
    /// DependencyPropertyHelper.GetValueSource.
    /// </summary>
    public struct ValueSource
    {
        internal ValueSource(BaseValueSourceInternal source, bool isExpression, bool isAnimated, bool isCoerced, bool isCurrent)
        {
            // this cast is justified because the public BaseValueSource enum
            // values agree with the internal BaseValueSourceInternal enum values.
            _baseValueSource = (BaseValueSource)source;

            _isExpression = isExpression;
            _isAnimated = isAnimated;
            _isCoerced = isCoerced;
            _isCurrent = isCurrent;
        }

        /// <summary>
        /// The base value source.
        /// </summary>
        public BaseValueSource BaseValueSource
        {
            get { return _baseValueSource; }
        }

        /// <summary>
        /// True if the value came from an Expression.
        /// </summary>
        public bool IsExpression
        {
            get { return _isExpression; }
        }

        /// <summary>
        /// True if the value came from an animation.
        /// </summary>
        public bool IsAnimated
        {
            get { return _isAnimated; }
        }

        /// <summary>
        /// True if the value was coerced.
        /// </summary>
        public bool IsCoerced
        {
            get { return _isCoerced; }
        }

        /// <summary>
        /// True if the value was set by SetCurrentValue.
        /// </summary>
        public bool IsCurrent
        {
            get { return _isCurrent; }
        }

        #region Object overrides - required by FxCop

        /// <summary>
        /// Return the hash code for this ValueSource.
        /// </summary>
        public override int GetHashCode()
        {
            return _baseValueSource.GetHashCode();
        }

        /// <summary>
        /// True if this ValueSource equals the argument.
        /// </summary>
        public override bool Equals(object o)
        {
            if (o is ValueSource)
            {
                ValueSource that = (ValueSource)o;

                return  this._baseValueSource == that._baseValueSource &&
                        this._isExpression == that._isExpression &&
                        this._isAnimated == that._isAnimated &&
                        this._isCoerced == that._isCoerced;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// True if the two arguments are equal.
        /// </summary>
        public static bool operator==(ValueSource vs1, ValueSource vs2)
        {
            return vs1.Equals(vs2);
        }

        /// <summary>
        /// True if the two arguments are unequal.
        /// </summary>
        public static bool operator!=(ValueSource vs1, ValueSource vs2)
        {
            return !vs1.Equals(vs2);
        }

        #endregion Object overrides - required by FxCop

        BaseValueSource _baseValueSource;
        bool            _isExpression;
        bool            _isAnimated;
        bool            _isCoerced;
        bool            _isCurrent;
    }

    /// <summary>
    /// Helper class for miscellaneous framework-level features related
    /// to DependencyProperties.
    /// </summary>
    public static class DependencyPropertyHelper
    {
        /// <summary>
        /// Return the source of the value for the given property.
        /// </summary>
        public static ValueSource GetValueSource(DependencyObject dependencyObject, DependencyProperty dependencyProperty)
        {
            ArgumentNullException.ThrowIfNull(dependencyObject);
            ArgumentNullException.ThrowIfNull(dependencyProperty);

            dependencyObject.VerifyAccess();

            bool hasModifiers, isExpression, isAnimated, isCoerced, isCurrent;
            BaseValueSourceInternal source = dependencyObject.GetValueSource(dependencyProperty, null, out hasModifiers, out isExpression, out isAnimated, out isCoerced, out isCurrent);

            return new ValueSource(source, isExpression, isAnimated, isCoerced, isCurrent);
        }

        /// <summary>
        /// Returns true if the given element belongs to an instance of a template
        /// that defines a value for the given property that may change during runtime
        /// based on changes elsewhere.
        /// For example, values set by Binding, TemplateBinding, or DynamicResource.
        /// </summary>
        /// <param name="elementInTemplate">element belonging to a template instance</param>
        /// <param name="dependencyProperty">property</param>
        /// <remarks>
        /// This method provides more detailed information in cases where
        /// the BaseValueSource is ParentTemplate.  The information is primarily
        /// of use to diagnostic tools.
        /// </remarks>
        public static bool IsTemplatedValueDynamic(DependencyObject elementInTemplate, DependencyProperty dependencyProperty)
        {
            ArgumentNullException.ThrowIfNull(elementInTemplate);
            ArgumentNullException.ThrowIfNull(dependencyProperty);

            FrameworkObject child = new FrameworkObject(elementInTemplate);
            DependencyObject templatedParent = child.TemplatedParent;

            if (templatedParent == null)
            {
                throw new ArgumentException(SR.ElementMustBelongToTemplate, nameof(elementInTemplate));
            }

            int templateChildIndex = child.TemplateChildIndex;
            return StyleHelper.IsValueDynamic(templatedParent, templateChildIndex, dependencyProperty);
        }
    }
}
