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

#nullable disable

using System.Runtime.InteropServices.ComTypes;
using Com = Windows.Win32.System.Com;
using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;

namespace System.Windows.Forms.Tests;

public unsafe partial class DataObjectTests
{
    private delegate IDataObject CreateManagedDataObjectForOutgoingDropData(Com.IDataObject* dataObject);

    [WinFormsFact]
    public void DataObject_CustomIDataObject_MockRoundTrip()
    {
        CustomIDataObject data = new();
        dynamic accessor = typeof(Control).TestAccessor.Dynamic;
        var dropTargetAccessor = typeof(DropTarget).TestAccessor;

        IComDataObject inData = accessor.CreateRuntimeDataObjectForDrag(data);
        inData.Should().BeAssignableTo<DataObject>();
        inData.Should().NotBeSameAs(data);

        // Simulate COM call. The COM call will eventually hit CreateManagedDataObjectForOutgoingDropData.
        // Note that this will be a ComWrappers created object since data has been wrapped in our DataObject.
        var inDataPtr = ComHelpers.GetComScope<Com.IDataObject>(inData);
        IDataObject outData = dropTargetAccessor.CreateDelegate<CreateManagedDataObjectForOutgoingDropData>()(inDataPtr);

        outData.Should().NotBeSameAs(data);
    }

    [WinFormsFact]
    public void DataObject_ComTypesIDataObject_MockRoundTrip()
    {
        CustomComTypesDataObject data = new();
        dynamic accessor = typeof(Control).TestAccessor.Dynamic;
        var dropTargetAccessor = typeof(DropTarget).TestAccessor;

        DataObject inData = accessor.CreateRuntimeDataObjectForDrag(data);
        inData.Should().NotBeSameAs(data);

        // Simulate COM call. The COM call will eventually hit CreateManagedDataObjectForOutgoingDropData.
        // Note that this will not be a ComWrappers created object since IComDataObject does not get wrapped in our DataObject.
        var inDataPtr = ComHelpers.GetComScope<Com.IDataObject>(inData);
        IDataObject outData = dropTargetAccessor.CreateDelegate<CreateManagedDataObjectForOutgoingDropData>()(inDataPtr);

        outData.Should().BeSameAs(inData);
    }

    private class CustomIDataObject : IDataObject
    {
        public object GetData(string format, bool autoConvert) => throw new NotImplementedException();
        public object GetData(string format) => throw new NotImplementedException();
        public object GetData(Type format) => throw new NotImplementedException();
        public bool GetDataPresent(string format, bool autoConvert) => throw new NotImplementedException();
        public bool GetDataPresent(string format) => throw new NotImplementedException();
        public bool GetDataPresent(Type format) => throw new NotImplementedException();
        public string[] GetFormats(bool autoConvert) => throw new NotImplementedException();
        public string[] GetFormats() => throw new NotImplementedException();
        public void SetData(string format, bool autoConvert, object data) => throw new NotImplementedException();
        public void SetData(string format, object data) => throw new NotImplementedException();
        public void SetData(Type format, object data) => throw new NotImplementedException();
        public void SetData(object data) => throw new NotImplementedException();
    }

    private class CustomComTypesDataObject : IComDataObject
    {
        public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) => throw new NotImplementedException();
        public void DUnadvise(int connection) => throw new NotImplementedException();
        public int EnumDAdvise(out IEnumSTATDATA enumAdvise) => throw new NotImplementedException();
        public IEnumFORMATETC EnumFormatEtc(DATADIR direction) => throw new NotImplementedException();
        public int GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) => throw new NotImplementedException();
        public void GetData(ref FORMATETC format, out STGMEDIUM medium) => throw new NotImplementedException();
        public void GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) => throw new NotImplementedException();
        public int QueryGetData(ref FORMATETC format) => throw new NotImplementedException();
        public void SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) => throw new NotImplementedException();
    }
}
