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

using System.ComponentModel;
using System.ComponentModel.Design;

namespace System.Windows.Forms;

public partial class ToolStripControlHost
{
    /// <devdoc>
    ///  Our implementation of ISite:
    ///  Since the Control which is wrapped by ToolStripControlHost is a runtime instance, there is no way of knowing
    ///  whether the control is in runtime or designtime.
    ///  This implementation of ISite would be set to Control.Site when ToolStripControlHost.Site is set at DesignTime.
    ///  (Refer to Site property on ToolStripControlHost) This implementation just returns the DesignMode property
    ///  to be ToolStripControlHost's DesignMode property. Everything else is pretty much default implementation.
    /// </devdoc>
    private class StubSite : ISite, IDictionaryService
    {
        private Dictionary<object, object>? _dictionary;
        private readonly IComponent _comp;
        private readonly IComponent _owner;

        public StubSite(IComponent control, IComponent host)
        {
            _comp = control;
            _owner = host;
        }

        /// <summary>
        ///  When implemented by a class, gets the component associated with the <see cref="ISite"/>.
        /// </summary>
        IComponent ISite.Component => _comp;

        /// <summary>
        ///  When implemented by a class, gets the container associated with the <see cref="ISite"/>.
        /// </summary>
        IContainer? ISite.Container => _owner.Site?.Container;

        /// <summary>
        ///  When implemented by a class, determines whether the component is in design mode.
        /// </summary>
        bool ISite.DesignMode => _owner.Site?.DesignMode ?? false;

        /// <summary>
        ///  When implemented by a class, gets or sets the name of
        ///  the component associated with the <see cref="ISite"/>.
        /// </summary>
        string? ISite.Name
        {
            get => _owner.Site?.Name;

            [RequiresUnreferencedCode(TrimmingConstants.SiteNameMessage)]
            set
            {
                _owner.Site?.Name = value;
            }
        }

        /// <summary>
        ///  Returns the requested service.
        /// </summary>
        object? IServiceProvider.GetService(Type service)
        {
            ArgumentNullException.ThrowIfNull(service);

            // We have to implement our own dictionary service. If we don't,
            // the properties of the underlying component will end up being
            // overwritten by our own properties when GetProperties is called
            return service == typeof(IDictionaryService) ? this : _owner.Site?.GetService(service);
        }

        /// <summary>
        ///  Retrieves the key corresponding to the given value.
        /// </summary>
        object? IDictionaryService.GetKey(object? value)
        {
            if (value is null || _dictionary is null)
            {
                return null;
            }

            foreach (var item in _dictionary)
            {
                object o = item.Value;
                if (value.Equals(o))
                {
                    return item.Key;
                }
            }

            return null;
        }

        /// <summary>
        ///  Retrieves the value corresponding to the given key.
        /// </summary>
        object? IDictionaryService.GetValue(object key)
        {
            object? value = null;
            _dictionary?.TryGetValue(key, out value);
            return value;
        }

        /// <summary>
        ///  Stores the given key-value pair in an object's site. This key-value
        ///  pair is stored on a per-object basis, and is a handy place to save
        ///  additional information about a component.
        /// </summary>
        void IDictionaryService.SetValue(object key, object? value)
        {
            _dictionary ??= [];
            ArgumentNullException.ThrowIfNull(key);

            if (value is null)
            {
                _dictionary.Remove(key);
            }
            else
            {
                _dictionary[key] = value;
            }
        }
    }
}
