﻿// 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.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim
{
    internal partial class CSharpProjectShim : ICSharpVenusProjectSite
    {
        public void AddReferenceToCodeDirectory(string assemblyFileName, ICSharpProjectRoot project)
            => AddReferenceToCodeDirectoryEx(assemblyFileName, project, CompilerOptions.OPTID_IMPORTS);

        public void RemoveReferenceToCodeDirectory(string assemblyFileName, ICSharpProjectRoot project)
        {
            var projectSite = GetProjectSite(project);

            var projectReferencesToRemove = ProjectSystemProject.GetProjectReferences().Where(p => p.ProjectId == projectSite.ProjectSystemProject.Id).ToList();

            if (projectReferencesToRemove.Count == 0)
            {
                throw new ArgumentException($"The project {nameof(project)} is not currently referenced by this project.");
            }

            foreach (var projectReferenceToRemove in projectReferencesToRemove)
            {
                ProjectSystemProject.RemoveProjectReference(new ProjectReference(projectSite.ProjectSystemProject.Id));
            }
        }

        public void OnDiskFileUpdated(string filename, ref System.Runtime.InteropServices.ComTypes.FILETIME pFT)
            => throw new NotImplementedException();

        public void OnCodeDirectoryAliasesChanged(ICSharpProjectRoot project, int previousAliasesCount, string[] previousAliases, int currentAliasesCount, string[] currentAliases)
        {
            var projectSite = GetProjectSite(project);

            using (ProjectSystemProject.CreateBatchScope())
            {
                var existingProjectReference = ProjectSystemProject.GetProjectReferences().Single(p => p.ProjectId == projectSite.ProjectSystemProject.Id);

                ProjectSystemProject.RemoveProjectReference(existingProjectReference);
                ProjectSystemProject.AddProjectReference(new ProjectReference(existingProjectReference.ProjectId, ImmutableArray.Create(currentAliases), existingProjectReference.EmbedInteropTypes));
            }
        }

        public void AddReferenceToCodeDirectoryEx(string assemblyFileName, ICSharpProjectRoot projectRoot, CompilerOptions optionID)
        {
            var projectSite = GetProjectSite(projectRoot);

            ProjectSystemProject.AddProjectReference(new ProjectReference(projectSite.ProjectSystemProject.Id, embedInteropTypes: optionID == CompilerOptions.OPTID_IMPORTSUSINGNOPIA));
        }

        /// <summary>
        /// Given a ICSharpProjectRoot instance, it returns the ProjectSite instance, throwing if it
        /// could not be obtained.
        /// </summary>
        private static CSharpProjectShim GetProjectSite(ICSharpProjectRoot project)
        {
            // Get the host back for the project
            var projectSiteGuid = typeof(ICSharpProjectSite).GUID;

            // We should have gotten a ProjectSite back. If we didn't, that means we're being given
            // a project site that we didn't get BindToProject called on first which is a no-no by
            // the project system.
            if (project.GetProjectSite(ref projectSiteGuid) is not CSharpProjectShim projectSite)
            {
                throw new ArgumentException($"{project} was not properly sited with the language service.", nameof(project));
            }

            return projectSite;
        }
    }
}
