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

#nullable disable

using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel
{
    internal sealed partial class CodeModelProjectCache
    {
        private readonly struct CacheEntry
        {
            // NOTE: The logic here is a little bit tricky.  We can't just keep a WeakReference to
            // something like a ComHandle, since it's not something that our clients keep alive.
            // instead, we keep a weak reference to the inner managed object, which we know will
            // always be alive if the outer aggregate is alive.  We can't just keep a WeakReference
            // to the RCW for the outer object either, since in cases where we have a DCOM or native
            // client, the RCW will be cleaned up, even though there is still a native reference
            // to the underlying native outer object.
            //
            // Instead we make use of an implementation detail of the way the CLR's COM aggregation 
            // works.  Namely, if all references to the aggregated object are released, the CLR 
            // responds to QI's for IUnknown with a different object.  So, we store the original
            // value, when we know that we have a client, and then we use that to compare to see
            // if we still have a client alive.
            //
            // NOTE: This is _NOT_ AddRef'd.  We use it just to store the integer value of the
            // IUnknown for comparison purposes.
            private readonly WeakComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> _fileCodeModelWeakComHandle;

            public CacheEntry(ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> handle)
                => _fileCodeModelWeakComHandle = new WeakComHandle<EnvDTE80.FileCodeModel2, FileCodeModel>(handle);

            public EnvDTE80.FileCodeModel2 FileCodeModelRcw
            {
                get
                {
                    return _fileCodeModelWeakComHandle.ComAggregateObject;
                }
            }

            internal bool TryGetFileCodeModelInstanceWithoutCaringWhetherRcwIsAlive(out FileCodeModel fileCodeModel)
                => _fileCodeModelWeakComHandle.TryGetManagedObjectWithoutCaringWhetherNativeObjectIsAlive(out fileCodeModel);

            public ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel>? ComHandle
            {
                get
                {
                    return _fileCodeModelWeakComHandle.ComHandle;
                }
            }
        }
    }
}
