// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.CodeDom;
using System.Globalization;

namespace System.ComponentModel.Design.Serialization;

/// <summary>
///  This serializer replaces the property serializer for properties when we're
///  in localization mode.
/// </summary>
internal class ResourcePropertyMemberCodeDomSerializer : MemberCodeDomSerializer
{
    private readonly CodeDomLocalizationModel _model;
    private readonly MemberCodeDomSerializer _serializer;
    private CultureInfo? _localizationLanguage;

    internal ResourcePropertyMemberCodeDomSerializer(MemberCodeDomSerializer serializer, CodeDomLocalizationModel model)
    {
        _serializer = serializer;
        _model = model;
    }

    /// <summary>
    ///  This method actually performs the serialization. When the member is serialized
    ///  the necessary statements will be added to the statements collection.
    /// </summary>
    public override void Serialize(IDesignerSerializationManager manager, object value, MemberDescriptor descriptor, CodeStatementCollection statements)
    {
        // We push the localization model to indicate that our serializer is in control. Our
        // serialization provider looks for this and decides what type of resource serializer
        // to give us.
        manager.Context.Push(_model);

        try
        {
            _serializer.Serialize(manager, value, descriptor, statements);
        }
        finally
        {
            manager.Context.Pop();
        }
    }

    private CultureInfo? GetLocalizationLanguage(IDesignerSerializationManager manager)
    {
        if (_localizationLanguage is null)
        {
            // Check to see if our base component's localizable prop is true
            if (manager.TryGetContext(out RootContext? rootCtx))
            {
                object comp = rootCtx.Value;
                PropertyDescriptor? prop = TypeDescriptor.GetProperties(comp)["LoadLanguage"];
                _localizationLanguage = prop?.GetValue<CultureInfo>(comp);
            }
        }

        return _localizationLanguage;
    }

    private void OnSerializationComplete(object? sender, EventArgs e)
    {
        // we do the cleanup here and clear out the cache of the localizedlanguage
        _localizationLanguage = null;

        // unhook the event
        IDesignerSerializationManager? manager = sender as IDesignerSerializationManager;
        Debug.Assert(manager is not null, "manager should not be null!");
        manager?.SerializationComplete -= OnSerializationComplete;
    }

    /// <summary>
    ///  This method returns true if the given member descriptor should be serialized,
    ///  or false if there is no need to serialize the member.
    /// </summary>
    public override bool ShouldSerialize(IDesignerSerializationManager manager, object value, MemberDescriptor descriptor)
    {
        bool shouldSerialize = _serializer.ShouldSerialize(manager, value, descriptor);

        if (!shouldSerialize && !descriptor.Attributes.Contains(DesignOnlyAttribute.Yes))
        {
            switch (_model)
            {
                case CodeDomLocalizationModel.PropertyReflection:
                    if (!shouldSerialize)
                    {
                        // hook up the event the first time to clear out our cache at the end of the serialization
                        if (_localizationLanguage is null)
                        {
                            manager.SerializationComplete += OnSerializationComplete;
                        }

                        if (GetLocalizationLanguage(manager) != CultureInfo.InvariantCulture)
                        {
                            shouldSerialize = true;
                        }
                    }

                    break;

                case CodeDomLocalizationModel.PropertyAssignment:
                    // If this property contains its default value, we still want to serialize it if we are in
                    // localization mode if we are writing to the default culture, but only if the object
                    // is not inherited.
                    if (!manager.TryGetContext(out InheritanceAttribute? inheritance) && !TypeDescriptorHelper.TryGetAttribute(value, out inheritance))
                    {
                        inheritance = InheritanceAttribute.NotInherited;
                    }

                    if (inheritance.InheritanceLevel != InheritanceLevel.InheritedReadOnly)
                    {
                        shouldSerialize = true;
                    }

                    break;

                default:
                    break;
            }
        }

        return shouldSerialize;
    }
}
