// 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 System;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy;
using Microsoft.VisualStudio.Shell.Interop;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim;

/// <summary>
/// The representation of a project to both the project factory and workspace API.
/// </summary>
/// <remarks>
/// Due to the number of interfaces this object must implement, all interface implementations
/// are in a separate files. Methods that are shared across multiple interfaces (which are
/// effectively methods that just QI from one interface to another), are implemented here.
/// </remarks>
internal sealed partial class CSharpProjectShim : AbstractLegacyProject, ICodeModelInstanceFactory
{
    /// <summary>
    /// This member is used to store a raw array of warning numbers, which is needed to properly implement
    /// ICSCompilerConfig.GetWarnNumbers. Read the implementation of that function for more details.
    /// </summary>
    private readonly IntPtr _warningNumberArrayPointer;

    private ICSharpProjectRoot _projectRoot;

    private readonly IServiceProvider _serviceProvider;

    /// <summary>
    /// Fetches the options processor for this C# project. Equivalent to the underlying member, but fixed to the derived type.
    /// </summary>
    private new OptionsProcessor ProjectSystemProjectOptionsProcessor
    {
        get => (OptionsProcessor)base.ProjectSystemProjectOptionsProcessor;
        set => base.ProjectSystemProjectOptionsProcessor = value;
    }

    public CSharpProjectShim(
        ICSharpProjectRoot projectRoot,
        string projectSystemName,
        IVsHierarchy hierarchy,
        IServiceProvider serviceProvider,
        IThreadingContext threadingContext)
        : base(projectSystemName,
               hierarchy,
               LanguageNames.CSharp,
               isVsIntellisenseProject: projectRoot is IVsIntellisenseProject,
               serviceProvider,
               threadingContext,
               externalErrorReportingPrefix: "CS")
    {
        _projectRoot = projectRoot;
        _serviceProvider = serviceProvider;
        _warningNumberArrayPointer = Marshal.AllocHGlobal(0);

        var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel));

        this.ProjectCodeModel = componentModel.GetService<IProjectCodeModelFactory>().CreateProjectCodeModel(ProjectSystemProject.Id, this);
        this.ProjectSystemProjectOptionsProcessor = new OptionsProcessor(this.ProjectSystemProject, Workspace.Services.SolutionServices);

        // Ensure the default options are set up
        ResetAllOptions();
    }

    public override void Disconnect()
    {
        _projectRoot = null;

        base.Disconnect();
    }

    ~CSharpProjectShim()
    {
        // Free the unmanaged memory we allocated in the constructor
        Marshal.FreeHGlobal(_warningNumberArrayPointer);

        // Free any entry point strings.
        if (_startupClasses != null)
        {
            foreach (var @class in _startupClasses)
            {
                Marshal.FreeHGlobal(@class);
            }
        }
    }

    EnvDTE.FileCodeModel ICodeModelInstanceFactory.TryCreateFileCodeModelThroughProjectSystem(string filePath)
    {
        if (_projectRoot.CanCreateFileCodeModel(filePath))
        {
            var iid = VSConstants.IID_IUnknown;
            return _projectRoot.CreateFileCodeModel(filePath, ref iid) as EnvDTE.FileCodeModel;
        }
        else
        {
            return null;
        }
    }
}
