﻿// 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.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Collections;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements
{
    [ComVisible(true)]
    [ComDefaultInterface(typeof(EnvDTE80.CodeImport))]
    public sealed class CodeImport : AbstractCodeElement, EnvDTE80.CodeImport
    {
        internal static EnvDTE80.CodeImport Create(
            CodeModelState state,
            FileCodeModel fileCodeModel,
            AbstractCodeElement parent,
            string dottedName)
        {
            var element = new CodeImport(state, fileCodeModel, parent, dottedName);
            var result = (EnvDTE80.CodeImport)ComAggregate.CreateAggregatedObject(element);

            return result;
        }

        internal static EnvDTE80.CodeImport CreateUnknown(
            CodeModelState state,
            FileCodeModel fileCodeModel,
            int nodeKind,
            string dottedName)
        {
            var element = new CodeImport(state, fileCodeModel, nodeKind, dottedName);
            return (EnvDTE80.CodeImport)ComAggregate.CreateAggregatedObject(element);
        }

        private readonly ParentHandle<AbstractCodeElement> _parentHandle; // parent object -- if parent is FCM then NULL else ref'd
        private readonly string _dottedName;

        private CodeImport(
            CodeModelState state,
            FileCodeModel fileCodeModel,
            AbstractCodeElement parent,
            string dottedName)
            : base(state, fileCodeModel)
        {
            _parentHandle = new ParentHandle<AbstractCodeElement>(parent);
            _dottedName = dottedName;
        }

        private CodeImport(
            CodeModelState state,
            FileCodeModel fileCodeModel,
            int nodeKind,
            string dottedName)
            : base(state, fileCodeModel, nodeKind)
        {
            _dottedName = dottedName;
        }

        internal override bool TryLookupNode(out SyntaxNode node)
        {
            node = null;

            var parentNode = _parentHandle.Value != null
                ? _parentHandle.Value.LookupNode()
                : FileCodeModel.GetSyntaxRoot();

            if (parentNode == null)
            {
                return false;
            }

            if (!CodeModelService.TryGetImportNode(parentNode, _dottedName, out var importNode))
            {
                return false;
            }

            node = importNode;
            return node != null;
        }

        internal override ISymbol LookupSymbol()
        {
            Debug.Fail("CodeImports aren't backed by symbols");
            throw new InvalidOperationException();
        }

        public override EnvDTE.vsCMElement Kind
        {
            get { return EnvDTE.vsCMElement.vsCMElementImportStmt; }
        }

        public override object Parent
        {
            get
            {
                if (_parentHandle.Value != null)
                {
                    return _parentHandle.Value;
                }
                else
                {
                    return FileCodeModel;
                }
            }
        }

        public string Alias
        {
            get
            {
                return CodeModelService.GetImportAlias(LookupNode());
            }

            set
            {
                // TODO: Implement
                throw new NotImplementedException();
            }
        }

        public string Namespace
        {
            get
            {
                return CodeModelService.GetImportNamespaceOrType(LookupNode());
            }

            set
            {
                // TODO: Implement
                throw new NotImplementedException();
            }
        }

        public override EnvDTE.CodeElements Children
        {
            get { return EmptyCollection.Create(this.State, this); }
        }

        protected override string GetName()
            => CodeModelService.GetName(LookupNode());

        protected override void SetName(string value)
            => throw Exceptions.ThrowEFail();

        protected override string GetFullName()
            => CodeModelService.GetFullName(LookupNode(), semanticModel: null);
    }
}
