<commit_before>#region Apache License, Version 2.0
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
#endregion

#region Using
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
//using System.Web.Services.Protocols;
using System.Windows.Forms;
using System.Xml;
using EnvDTE;
using EnvDTE80;
using Extensibility;
using log4net;
using log4net.Config;
using log4net.Core;
using log4net.Repository.Hierarchy;
using Microsoft.VisualStudio.CommandBars;
using NPanday.Artifact;
using NPanday.Model.Pom;
using NPanday.ProjectImporter.Parser.VisualStudioProjectTypes;
using NPanday.Utils;
using NPanday.VisualStudio.Addin.Commands;
using NPanday.VisualStudio.Addin.Helper;
using VSLangProj;
using VSLangProj80;

#endregion

namespace NPanday.VisualStudio.Addin
{

    #region Connect
    /// <summary>The object for implementing an Add-in.</summary>
    /// <seealso class='IDTExtensibility2' />
    public class Connect : IDTExtensibility2, IDTCommandTarget, IWebServicesRefUtils
    {
        #region Connect()
        /// <summary>
        /// Implements the constructor for the Add-in object.
        /// Place your initialization code within this method.
        /// </summary>
        public Connect()
        {
        }
        #endregion
        #region Clearing OutputWindow upon closing solution
        //to hold eventhandler for solution
        private static EnvDTE.SolutionEvents globalSolutionEvents;

        //to hold eventhandler for projects
        private static EnvDTE.ProjectItemsEvents projectItemsEvents;

        void SolutionEvents_BeforeClosing()
        {
            mavenRunner.ClearOutputWindow();
            outputWindowPane.Clear();
        }

        void SolutionEvents_Opened()
        {
            if (_applicationObject != null && _applicationObject.Solution != null)
            {
                attachReferenceEvent();
            }
        }

        void SolutionEvents_ProjectAdded(EnvDTE.Project project)
        {
            if (_applicationObject != null && _applicationObject.Solution != null)
            {
                attachReferenceEvent();
            }
        }

        //to hold eventhandler for projectItemsEvents
        void ProjectItemEvents_ItemAdded(ProjectItem projectItem)
        {
            if (_applicationObject != null && projectItem != null)
            {
                PomHelperUtility pomUtil = createPomUtility(projectItem.ContainingProject);
                if (pomUtil == null)
                    return;

                // added configuration when including web or service reference

                if (!pomUtil.isWebRefExisting(projectItem.Name))
                {
                    string refType = IsWebReference(projectItem) ? Messages.MSG_D_WEB_REF : Messages.MSG_D_SERV_REF;
                    String reference = GetReference(projectItem, refType);
                    if (reference != null)
                    {
                        addWebReference(pomUtil, projectItem.Name, refType + "\\" + projectItem.Name + "\\" + reference, string.Empty);
                    }
                }

                //determine which plugin the projectItem belongs to

                if (projectItem.Name.Contains(".cs") || projectItem.Name.Contains(".vb"))
                {
                    //change addpluginConfiguration to accept xmlElement instead
                    pomUtil.AddMavenCompilePluginConfiguration("org.apache.npanday.plugins", "maven-compile-plugin", "includeSources", "includeSource", GetRelativePathToProject(projectItem, projectItem.Name));
                }

                if (projectItem.Name.Contains(".resx"))
                {
                    string resxName = projectItem.ContainingProject.Name + "." + projectItem.Name.Replace(".resx", "");

                    //check if resx plugin already exists
                    if (!pomUtil.HasPlugin("org.apache.npanday.plugins", "maven-resgen-plugin"))
                    {
                        try
                        {
                            XmlDocument xmlDocument = new XmlDocument();

                            PluginConfiguration pluginConf = new PluginConfiguration();

                            XmlElement nodeCollection = xmlDocument.CreateElement("embeddedResources", @"http://maven.apache.org/POM/4.0.0");
                            //XmlNode nodeCollection = xmlDocument.CreateNode(XmlNodeType.Element, "embeddededResources", @"http://maven.apache.org/POM/4.0.0");
                            XmlNode node = xmlDocument.CreateNode(XmlNodeType.Element, "embeddededResource", @"http://maven.apache.org/POM/4.0.0");
                            XmlNode nodeSrc = xmlDocument.CreateNode(XmlNodeType.Element, "sourceFile", @"http://maven.apache.org/POM/4.0.0");
                            XmlNode nodeName = xmlDocument.CreateNode(XmlNodeType.Element, "name", @"http://maven.apache.org/POM/4.0.0");

                            nodeSrc.InnerText = projectItem.Name;
                            nodeName.InnerText = resxName;
                            node.AppendChild(nodeSrc);
                            node.AppendChild(nodeName);
                            nodeCollection.AppendChild(node);
                            List<XmlElement> anyHolder = new List<XmlElement>();
                            anyHolder.Add(nodeCollection);
                            pluginConf.Any = anyHolder.ToArray();

                            pomUtil.AddPlugin("org.apache.npanday.plugins", "maven-resgen-plugin", null, true, pluginConf);
                        }
                        catch (Exception e)
                        {
                            //Suppressed Exception for malfored pom
                        }

                    }
                    //add plugin conifguration
                    else
                    {
                        pomUtil.AddMavenResxPluginConfiguration("org.apache.npanday.plugins", "maven-resgen-plugin", "embeddedResources", "embeddedResource", projectItem.Name, resxName);
                    }
                }
            }
        }

        private PomHelperUtility createPomUtility(Project project)
        {
            PomHelperUtility util = null;
            if (_applicationObject != null && _applicationObject.Solution != null)
            {
                try
                {
                    if (_applicationObject.Solution.FullName != null && _applicationObject.Solution.FullName.Length > 0)
                    {
                        util = new PomHelperUtility(new FileInfo(_applicationObject.Solution.FullName), new FileInfo(project.FullName));
                    }
                    else
                    {
                        log.Debug("POM utility was requested before solution was created!");
                        util = new PomHelperUtility(new FileInfo(project.FullName), new FileInfo(project.FullName));
                    }
                }
                catch (Exception e)
                {
                    log.Debug("Not updating POM: " + e.Message, e);
                }
            }
            return util;
        }

        void ProjectItemEvents_ItemRemoved(ProjectItem projectItem)
        {
            if (_applicationObject != null && projectItem != null)
            {
                if (_applicationObject != null && projectItem != null)
                {
                    PomHelperUtility pomUtil = createPomUtility(projectItem.ContainingProject);
                    if (pomUtil == null)
                        return;

                    // remove web reference configuration in pom.xml when using "Exclude in Project"

                    string fullPath = projectItem.get_FileNames(1);
                    string refType = Messages.MSG_D_WEB_REF;

                    if (fullPath.StartsWith(Path.GetDirectoryName(projectItem.ContainingProject.FullName) + "\\" + Messages.MSG_D_SERV_REF))
                    {
                        refType = Messages.MSG_D_SERV_REF;
                    }

                    string reference = GetReference(projectItem, refType);
                    if (reference != null)
                    {
                        string path = GetReferencePath(projectItem, refType);
                        pomUtil.RemoveWebReference(path, projectItem.Name);
                    }

                    if (projectItem.Name.Contains(".cs") || projectItem.Name.Contains(".vb"))
                    {
                        //change addpluginConfiguration to accept xmlElement instead
                        pomUtil.RemoveMavenCompilePluginConfiguration("org.apache.npanday.plugins", "maven-compile-plugin", "includeSources", "includeSource", GetRelativePathToProject(projectItem, null));
                    }

                    if (projectItem.Name.Contains(".resx"))
                    {
                        string resxName = projectItem.ContainingProject.Name + "." + projectItem.Name.Replace(".resx", "");
                        pomUtil.RemoveMavenResxPluginConfiguration("org.apache.npanday.plugins", "maven-resgen-plugin", "embeddedResources", "embeddedResource", projectItem.Name, resxName);
                    }
                }
            }

        }

        void ProjectItemEvents_ItemRenamed(ProjectItem projectItem, string oldName)
        {
            if (_applicationObject != null && projectItem != null)
            {
                PomHelperUtility pomUtil = createPomUtility(projectItem.ContainingProject);
                if (pomUtil != null)
                {
                    if (projectItem.Name.Contains(".cs") || projectItem.Name.Contains(".vb"))
                    {
                        //change addpluginConfiguration to accept xmlElement instead
                        pomUtil.RenameMavenCompilePluginConfiguration("org.apache.npanday.plugins", "maven-compile-plugin", "includeSources", "includeSource", GetRelativePathToProject(projectItem, oldName), GetRelativePathToProject(projectItem, null));
                    }

                    if (projectItem.Name.Contains(".resx"))
                    {
                        string resxName = projectItem.ContainingProject.Name + "." + projectItem.Name.Replace(".resx", "");
                        string oldResxName = projectItem.ContainingProject.Name + "." + oldName.Replace(".resx", "");
                        pomUtil.RenameMavenResxPluginConfiguration("org.apache.npanday.plugins", "maven-resgen-plugin", "embeddedResources", "embeddedResource", oldName, oldResxName, projectItem.Name, resxName);
                    }
                }
            }

        }

        /// <summary>
        /// Returns either a relative path to project (if a project item is assciated with a file - like *.cs) or just the name of project item
        /// </summary>
        /// <param name="projectItem"></param>
        /// <returns></returns>
        private static string GetRelativePathToProject(ProjectItem projectItem, string fileName)
        {
            if (projectItem.FileCount == 1)
            {
                Uri fullPathUri = fileName == null ? new Uri(projectItem.get_FileNames(0)) : new Uri(Path.Combine(Path.GetDirectoryName(projectItem.get_FileNames(0)), fileName));
                Uri projectUri = new Uri(Path.GetDirectoryName(projectItem.ContainingProject.FullName) + Path.DirectorySeparatorChar);
                return projectUri.MakeRelativeUri(fullPathUri).ToString().Replace("%20", " ");
            }
            return projectItem.Name;
        }

        private static string GetReferencePath(ProjectItem projectItem, string refType)
        {
            return Path.GetDirectoryName(projectItem.ContainingProject.FullName) + "\\" + refType + "\\" + projectItem.Name;
        }

        private static string GetReference(ProjectItem projectItem, string refType)
        {
            string path = GetReferencePath(projectItem, refType);
            if (Directory.Exists(path))
            {
                string[] files = Directory.GetFiles(path, "*.wsdl");

                if (files.Length > 0)
                {
                    return Path.GetFileName(files[0]);
                }
            }

            return null;
        }

        private static bool IsWebReference(ProjectItem item)
        {
            if (item.ContainingProject.Object is VSProject)
            {
                ProjectItem webrefs = ((VSProject)item.ContainingProject.Object).WebReferencesFolder;
                if (webrefs != null && webrefs.ProjectItems != null)
                {
                    foreach (ProjectItem webref in webrefs.ProjectItems)
                    {
                        if (webref == item)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        #endregion
        static bool projectRefEventLoaded;

        #region OnConnection(object,ext_ConnectMode,object,Array)
        /// <summary>
        /// Used for SaveAllDocuments
        /// </summary>
        private CommandBarControl saveAllControl;



        /// <summary>
        /// Implements the OnConnection method of the IDTExtensibility2 interface.
        /// Receives notification that the Add-in is being loaded.
        /// </summary>
        /// <param term='application'>Root object of the host application.</param>
        /// <param term='connectMode'>Describes how the Add-in is being loaded.</param>
        /// <param term='addInInst'>Object representing this Add-in.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            if (_applicationObject == null)
            {
                // this should only be done once!

                _applicationObject = (DTE2)application;
                mavenRunner = new MavenRunner(_applicationObject);
                mavenRunner.RunnerStopped += new EventHandler(mavenRunner_RunnerStopped);
                _addInInstance = (AddIn)addInInst;
                mavenConnected = true;

                string paneName = "NPanday Build System";

                outputWindowPane = getOrCreateOutputPane(paneName);

                try
                {
                    // Currently, MSBuild uses the namespace NPanday.VisualStudio, while NPanday uses the artifactID NPanday.VisualStudio.AddIn for embedded resources. Workaround...
                    Stream fileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("NPanday.VisualStudio.log4net.xml");
                    if (fileStream == null)
                    {
                        fileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("NPanday.VisualStudio.Addin.log4net.xml");
                    }
                    if (fileStream != null)
                    {
                        XmlConfigurator.Configure(fileStream);
                    }
                    else
                    {
                        outputWindowPane.OutputString("Failed to configure logging subsystem: No log4net.xml configuration found\n");
                    }
                }
                catch (Exception e)
                {
                    outputWindowPane.OutputString("Failed to configure logging subsystem: " + e.Message + "\n");
                }

                Level level =  Level.Info;
                // TODO: should be a better way to enable this
                if (_addInInstance.Name.Contains("SNAPSHOT"))
                {
                    level = Level.Debug;
                }

                Hierarchy h = (Hierarchy)LogManager.GetRepository();
                Logger rootLogger = h.Root;
                OutputWindowPaneAppender appender = new OutputWindowPaneAppender(outputWindowPane, level);
                rootLogger.AddAppender(appender);

                log.Debug("Intialized panes");

                _buttonCommandRegistry = new ButtonCommandRegistry(_applicationObject, buildCommandContext);

                _finder = new VisualStudioControlsFinder(_applicationObject);
                _finder.IndexCommands();

                globalSolutionEvents = (EnvDTE.SolutionEvents)((Events2)_applicationObject.Events).SolutionEvents;
                globalSolutionEvents.BeforeClosing += new _dispSolutionEvents_BeforeClosingEventHandler(SolutionEvents_BeforeClosing);
                globalSolutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SolutionEvents_Opened);
                globalSolutionEvents.ProjectAdded += new _dispSolutionEvents_ProjectAddedEventHandler(SolutionEvents_ProjectAdded);

                projectItemsEvents = (EnvDTE.ProjectItemsEvents)((Events2)_applicationObject.Events).ProjectItemsEvents;
                projectItemsEvents.ItemAdded += new _dispProjectItemsEvents_ItemAddedEventHandler(ProjectItemEvents_ItemAdded);
                projectItemsEvents.ItemRemoved += new _dispProjectItemsEvents_ItemRemovedEventHandler(ProjectItemEvents_ItemRemoved);
                projectItemsEvents.ItemRenamed += new _dispProjectItemsEvents_ItemRenamedEventHandler(ProjectItemEvents_ItemRenamed);
            }

            log.Debug("OnConnection() called with connect mode " + connectMode);

            // this is only called once per installation
            if (connectMode == ext_ConnectMode.ext_cm_UISetup)
            {
                log.Debug("Registering Startup-Command in Tools-Menu");

                Command toolsMenuCommand = null;
                object[] contextGUIDS = new object[] { };
                Commands2 commands = (Commands2)_applicationObject.Commands;

                string toolsMenuName = VSCommandCaptions.Tools;

                //Place the command on the tools menu.
                //Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:
                CommandBar menuBarCommandBar = ((CommandBars)_applicationObject.CommandBars)["MenuBar"];

                //Find the Tools command bar on the MenuBar command bar:
                CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
                CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;

                if (toolsPopup == null)
                {
                    string message = "Will skip adding control, as the tools popup could not be found with name '" + toolsMenuName + "'";
                    log.Warn(message);
                    MessageBox.Show(message);
                }

                //This try/catch block can be duplicated if you wish to add multiple commands to be handled by your Add-in,
                //  just make sure you also update the QueryStatus/Exec method to include the new command names.
                try
                {
                    //Add a command to the Commands collection:
                    toolsMenuCommand = commands.AddNamedCommand2(_addInInstance, "NPandayAddin",
                        Messages.MSG_D_NPANDAY_BUILD_SYSTEM, Messages.MSG_T_NPANDAY_BUILDSYSTEM, true, 480, ref contextGUIDS,
                        (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled,
                        (int)vsCommandStyle.vsCommandStylePictAndText,
                        vsCommandControlType.vsCommandControlTypeButton);

                    //Add a control for the command to the tools menu:
                    if ((toolsMenuCommand != null) && (toolsPopup != null))
                    {
                        toolsMenuCommand.AddControl(toolsPopup.CommandBar, 1);
                    }
                    else
                    {
                        string message = "Skipped adding control as the NPanday start command could not be found.";
                        log.Warn(message);
                        MessageBox.Show(message);
                    }
                }
                catch (System.ArgumentException ex)
                {
                    //If we are here, then the exception is probably because a command with that name
                    //  already exists. If so there is no need to recreate the command and we can
                    //  safely ignore the exception.
                    log.Warn("Exception occured when adding NPanday to the Tools menu: " + ex.Message, ex);
                }

            }


            if (connectMode == ext_ConnectMode.ext_cm_AfterStartup)
            {
                launchNPandayBuildSystem();
            }
        }

        private OutputWindowPane getOrCreateOutputPane(string paneName)
        {
            Window win = _applicationObject.Windows.Item(EnvDTE.Constants.vsWindowKindOutput);
            OutputWindow outputWindow = (OutputWindow)win.Object;
            OutputWindowPanes panes = outputWindow.OutputWindowPanes;

            foreach (OutputWindowPane outputPane in panes)
            {
                if (outputPane.Name == paneName)
                {
                    return outputPane;
                }
            }
            return outputWindow.OutputWindowPanes.Add(paneName);
        }

        private IButtonCommandContext buildCommandContext()
        {
            return new ButtonCommandContext(this, _applicationObject);
        }

        private class ButtonCommandContext : IButtonCommandContext
        {
            private Connect _connect;
            private readonly DTE2 _application;

            public ButtonCommandContext(Connect connect, DTE2 application)
            {
                _connect = connect;
                _application = application;
            }

            public FileInfo CurrentSelectedProjectPom
            {
                get { return _connect.CurrentSelectedProjectPom; }
            }

            public ArtifactContext ArtifactContext
            {
                get { return _connect.container; }
            }

            public void ExecuteCommand(string visualStudioCommandName)
            {
                _application.ExecuteCommand(visualStudioCommandName, "");
            }

            public void ExecuteCommand<TCommand>() where TCommand : ButtonCommand, new()
            {
                _connect._buttonCommandRegistry.Excecute<TCommand>(this);
            }
        }

        void mavenRunner_RunnerStopped(object sender, EventArgs e)
        {
            //stopButton.Enabled = false;
        }

        public static bool IsWebSite(Project project)
        {
            return project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.Web_Site), StringComparison.OrdinalIgnoreCase);
        }

        public static bool IsWebProject(Project project)
        {
            // quick check for the right extender, in lieu of full GUID availability (see below)
            try
            {
                if (project.get_Extender("WebApplication") != null)
                {
                    return true;
                }
            }
            catch
            {
                // ignore
            }

            // project Kind is only the main one, so the below is not often correct. A more definitive query would be to get all the subtype GUIDs for a project (see http://www.mztools.com/articles/2007/mz2007016.aspx)
            return project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.Web_Site), StringComparison.OrdinalIgnoreCase) ||
                project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.Web_Application), StringComparison.OrdinalIgnoreCase);
        }

        public static bool IsCloudProject(Project project)
        {
            return project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.WindowsAzure_CloudService), StringComparison.OrdinalIgnoreCase);
        }

        private const string FOLDER_KIND_GUID = "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}";

        public static bool IsFolder(Project project)
        {
            // make sure there's a project item
            if (project == null)
                return false;

            // compare the project kind to the folder guid
            return (String.Compare(project.Kind, FOLDER_KIND_GUID, true) == 0);
        }

        void InsertKeyTag(string filePath, string key)
        {
            try
            {
                StreamReader reader = new StreamReader(filePath);
                List<String> contents = new List<string>();

                string temp = reader.ReadLine();
                contents.Add(temp);

                while (temp != null)
                {
                    temp = reader.ReadLine();
                    if (temp != null)
                    {
                        contents.Add(temp);
                        string includeSources = "</includeSources>";
                        if (temp.Contains(includeSources))
                        {
                            string keyNode = temp.Substring(0, temp.IndexOf(includeSources));
                            keyNode += string.Format("<keyfile>{0}</keyfile>", key);
                            contents.Add(keyNode);
                        }
                    }
                }
                reader.Close();

                TextWriter writer = new StreamWriter(filePath);

                foreach (string item in contents)
                {
                    writer.WriteLine(item);
                }

                writer.Close();
            }
            catch (Exception e)
            {
                log.Error(e.Message);
            }


        }

        void SigningEvents_SignatureAdded()
        {
            Solution2 solution = (Solution2)_applicationObject.Solution;
            string pomFilePath = string.Empty;
            foreach (Project project in solution.Projects)
            {
                String key = string.Empty;
                XmlDocument doc = new XmlDocument();
                bool isSigned = false;
                try
                {
                    //Reading the project file
                    doc.Load(project.FullName);

                    //construct the path for the pom file and check for file existance.
                    //return if file does not exist.
                    pomFilePath = project.FullName.Substring(0, project.FullName.LastIndexOf("\\"));
                    pomFilePath += "\\pom.xml";
                    if (!File.Exists(pomFilePath))
                    {
                        return;
                    }
                    XmlNodeList keyFileList = doc.GetElementsByTagName("AssemblyOriginatorKeyFile");
                    XmlNodeList signBoolList = doc.GetElementsByTagName("SignAssembly");

                    foreach (XmlNode item in signBoolList)
                    {
                        if (item.InnerText.Equals("true"))
                        {
                            isSigned = true;
                        }
                    }

                    if (isSigned)
                    {
                        foreach (XmlNode item in keyFileList)<commit_msg>avoid errors on startup because a project is not yet fully configured<commit_after>#region Apache License, Version 2.0
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
#endregion

#region Using
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
//using System.Web.Services.Protocols;
using System.Windows.Forms;
using System.Xml;
using EnvDTE;
using EnvDTE80;
using Extensibility;
using log4net;
using log4net.Config;
using log4net.Core;
using log4net.Repository.Hierarchy;
using Microsoft.VisualStudio.CommandBars;
using NPanday.Artifact;
using NPanday.Model.Pom;
using NPanday.ProjectImporter.Parser.VisualStudioProjectTypes;
using NPanday.Utils;
using NPanday.VisualStudio.Addin.Commands;
using NPanday.VisualStudio.Addin.Helper;
using VSLangProj;
using VSLangProj80;

#endregion

namespace NPanday.VisualStudio.Addin
{

    #region Connect
    /// <summary>The object for implementing an Add-in.</summary>
    /// <seealso class='IDTExtensibility2' />
    public class Connect : IDTExtensibility2, IDTCommandTarget, IWebServicesRefUtils
    {
        #region Connect()
        /// <summary>
        /// Implements the constructor for the Add-in object.
        /// Place your initialization code within this method.
        /// </summary>
        public Connect()
        {
        }
        #endregion
        #region Clearing OutputWindow upon closing solution
        //to hold eventhandler for solution
        private static EnvDTE.SolutionEvents globalSolutionEvents;

        //to hold eventhandler for projects
        private static EnvDTE.ProjectItemsEvents projectItemsEvents;

        void SolutionEvents_BeforeClosing()
        {
            mavenRunner.ClearOutputWindow();
            outputWindowPane.Clear();
        }

        void SolutionEvents_Opened()
        {
            if (_applicationObject != null && _applicationObject.Solution != null)
            {
                attachReferenceEvent();
            }
        }

        void SolutionEvents_ProjectAdded(EnvDTE.Project project)
        {
            if (_applicationObject != null && _applicationObject.Solution != null)
            {
                attachReferenceEvent();
            }
        }

        //to hold eventhandler for projectItemsEvents
        void ProjectItemEvents_ItemAdded(ProjectItem projectItem)
        {
            if (_applicationObject != null && projectItem != null)
            {
                PomHelperUtility pomUtil = createPomUtility(projectItem.ContainingProject);
                if (pomUtil == null)
                    return;

                // added configuration when including web or service reference

                if (!pomUtil.isWebRefExisting(projectItem.Name))
                {
                    string refType = IsWebReference(projectItem) ? Messages.MSG_D_WEB_REF : Messages.MSG_D_SERV_REF;
                    String reference = GetReference(projectItem, refType);
                    if (reference != null)
                    {
                        addWebReference(pomUtil, projectItem.Name, refType + "\\" + projectItem.Name + "\\" + reference, string.Empty);
                    }
                }

                //determine which plugin the projectItem belongs to

                if (projectItem.Name.Contains(".cs") || projectItem.Name.Contains(".vb"))
                {
                    //change addpluginConfiguration to accept xmlElement instead
                    pomUtil.AddMavenCompilePluginConfiguration("org.apache.npanday.plugins", "maven-compile-plugin", "includeSources", "includeSource", GetRelativePathToProject(projectItem, projectItem.Name));
                }

                if (projectItem.Name.Contains(".resx"))
                {
                    string resxName = projectItem.ContainingProject.Name + "." + projectItem.Name.Replace(".resx", "");

                    //check if resx plugin already exists
                    if (!pomUtil.HasPlugin("org.apache.npanday.plugins", "maven-resgen-plugin"))
                    {
                        try
                        {
                            XmlDocument xmlDocument = new XmlDocument();

                            PluginConfiguration pluginConf = new PluginConfiguration();

                            XmlElement nodeCollection = xmlDocument.CreateElement("embeddedResources", @"http://maven.apache.org/POM/4.0.0");
                            //XmlNode nodeCollection = xmlDocument.CreateNode(XmlNodeType.Element, "embeddededResources", @"http://maven.apache.org/POM/4.0.0");
                            XmlNode node = xmlDocument.CreateNode(XmlNodeType.Element, "embeddededResource", @"http://maven.apache.org/POM/4.0.0");
                            XmlNode nodeSrc = xmlDocument.CreateNode(XmlNodeType.Element, "sourceFile", @"http://maven.apache.org/POM/4.0.0");
                            XmlNode nodeName = xmlDocument.CreateNode(XmlNodeType.Element, "name", @"http://maven.apache.org/POM/4.0.0");

                            nodeSrc.InnerText = projectItem.Name;
                            nodeName.InnerText = resxName;
                            node.AppendChild(nodeSrc);
                            node.AppendChild(nodeName);
                            nodeCollection.AppendChild(node);
                            List<XmlElement> anyHolder = new List<XmlElement>();
                            anyHolder.Add(nodeCollection);
                            pluginConf.Any = anyHolder.ToArray();

                            pomUtil.AddPlugin("org.apache.npanday.plugins", "maven-resgen-plugin", null, true, pluginConf);
                        }
                        catch (Exception e)
                        {
                            //Suppressed Exception for malfored pom
                        }

                    }
                    //add plugin conifguration
                    else
                    {
                        pomUtil.AddMavenResxPluginConfiguration("org.apache.npanday.plugins", "maven-resgen-plugin", "embeddedResources", "embeddedResource", projectItem.Name, resxName);
                    }
                }
            }
        }

        private PomHelperUtility createPomUtility(Project project)
        {
            PomHelperUtility util = null;
            if (_applicationObject != null && _applicationObject.Solution != null)
            {
                try
                {
                    if (_applicationObject.Solution.FullName != null && _applicationObject.Solution.FullName.Length > 0)
                    {
                        util = new PomHelperUtility(new FileInfo(_applicationObject.Solution.FullName), new FileInfo(project.FullName));
                    }
                    else
                    {
                        log.Debug("POM utility was requested before solution was created!");
                        util = new PomHelperUtility(new FileInfo(project.FullName), new FileInfo(project.FullName));
                    }
                }
                catch (Exception e)
                {
                    log.Debug("Not updating POM: " + e.Message, e);
                }
            }
            return util;
        }

        void ProjectItemEvents_ItemRemoved(ProjectItem projectItem)
        {
            if (_applicationObject != null && projectItem != null)
            {
                if (_applicationObject != null && projectItem != null)
                {
                    PomHelperUtility pomUtil = createPomUtility(projectItem.ContainingProject);
                    if (pomUtil == null)
                        return;

                    // remove web reference configuration in pom.xml when using "Exclude in Project"

                    string fullPath = projectItem.get_FileNames(1);
                    string refType = Messages.MSG_D_WEB_REF;

                    if (fullPath.StartsWith(Path.GetDirectoryName(projectItem.ContainingProject.FullName) + "\\" + Messages.MSG_D_SERV_REF))
                    {
                        refType = Messages.MSG_D_SERV_REF;
                    }

                    string reference = GetReference(projectItem, refType);
                    if (reference != null)
                    {
                        string path = GetReferencePath(projectItem, refType);
                        pomUtil.RemoveWebReference(path, projectItem.Name);
                    }

                    if (projectItem.Name.Contains(".cs") || projectItem.Name.Contains(".vb"))
                    {
                        //change addpluginConfiguration to accept xmlElement instead
                        pomUtil.RemoveMavenCompilePluginConfiguration("org.apache.npanday.plugins", "maven-compile-plugin", "includeSources", "includeSource", GetRelativePathToProject(projectItem, null));
                    }

                    if (projectItem.Name.Contains(".resx"))
                    {
                        string resxName = projectItem.ContainingProject.Name + "." + projectItem.Name.Replace(".resx", "");
                        pomUtil.RemoveMavenResxPluginConfiguration("org.apache.npanday.plugins", "maven-resgen-plugin", "embeddedResources", "embeddedResource", projectItem.Name, resxName);
                    }
                }
            }

        }

        void ProjectItemEvents_ItemRenamed(ProjectItem projectItem, string oldName)
        {
            if (_applicationObject != null && projectItem != null)
            {
                PomHelperUtility pomUtil = createPomUtility(projectItem.ContainingProject);
                if (pomUtil != null)
                {
                    if (projectItem.Name.Contains(".cs") || projectItem.Name.Contains(".vb"))
                    {
                        //change addpluginConfiguration to accept xmlElement instead
                        pomUtil.RenameMavenCompilePluginConfiguration("org.apache.npanday.plugins", "maven-compile-plugin", "includeSources", "includeSource", GetRelativePathToProject(projectItem, oldName), GetRelativePathToProject(projectItem, null));
                    }

                    if (projectItem.Name.Contains(".resx"))
                    {
                        string resxName = projectItem.ContainingProject.Name + "." + projectItem.Name.Replace(".resx", "");
                        string oldResxName = projectItem.ContainingProject.Name + "." + oldName.Replace(".resx", "");
                        pomUtil.RenameMavenResxPluginConfiguration("org.apache.npanday.plugins", "maven-resgen-plugin", "embeddedResources", "embeddedResource", oldName, oldResxName, projectItem.Name, resxName);
                    }
                }
            }

        }

        /// <summary>
        /// Returns either a relative path to project (if a project item is assciated with a file - like *.cs) or just the name of project item
        /// </summary>
        /// <param name="projectItem"></param>
        /// <returns></returns>
        private static string GetRelativePathToProject(ProjectItem projectItem, string fileName)
        {
            if (projectItem.FileCount == 1)
            {
                Uri fullPathUri = fileName == null ? new Uri(projectItem.get_FileNames(0)) : new Uri(Path.Combine(Path.GetDirectoryName(projectItem.get_FileNames(0)), fileName));
                Uri projectUri = new Uri(Path.GetDirectoryName(projectItem.ContainingProject.FullName) + Path.DirectorySeparatorChar);
                return projectUri.MakeRelativeUri(fullPathUri).ToString().Replace("%20", " ");
            }
            return projectItem.Name;
        }

        private static string GetReferencePath(ProjectItem projectItem, string refType)
        {
            return Path.GetDirectoryName(projectItem.ContainingProject.FullName) + "\\" + refType + "\\" + projectItem.Name;
        }

        private static string GetReference(ProjectItem projectItem, string refType)
        {
            string path = GetReferencePath(projectItem, refType);
            if (Directory.Exists(path))
            {
                string[] files = Directory.GetFiles(path, "*.wsdl");

                if (files.Length > 0)
                {
                    return Path.GetFileName(files[0]);
                }
            }

            return null;
        }

        private static bool IsWebReference(ProjectItem item)
        {
            if (item.ContainingProject.Object is VSProject)
            {
                ProjectItem webrefs = ((VSProject)item.ContainingProject.Object).WebReferencesFolder;
                if (webrefs != null && webrefs.ProjectItems != null)
                {
                    foreach (ProjectItem webref in webrefs.ProjectItems)
                    {
                        if (webref == item)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        #endregion
        static bool projectRefEventLoaded;

        #region OnConnection(object,ext_ConnectMode,object,Array)
        /// <summary>
        /// Used for SaveAllDocuments
        /// </summary>
        private CommandBarControl saveAllControl;



        /// <summary>
        /// Implements the OnConnection method of the IDTExtensibility2 interface.
        /// Receives notification that the Add-in is being loaded.
        /// </summary>
        /// <param term='application'>Root object of the host application.</param>
        /// <param term='connectMode'>Describes how the Add-in is being loaded.</param>
        /// <param term='addInInst'>Object representing this Add-in.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            if (_applicationObject == null)
            {
                // this should only be done once!

                _applicationObject = (DTE2)application;
                mavenRunner = new MavenRunner(_applicationObject);
                mavenRunner.RunnerStopped += new EventHandler(mavenRunner_RunnerStopped);
                _addInInstance = (AddIn)addInInst;
                mavenConnected = true;

                string paneName = "NPanday Build System";

                outputWindowPane = getOrCreateOutputPane(paneName);

                try
                {
                    // Currently, MSBuild uses the namespace NPanday.VisualStudio, while NPanday uses the artifactID NPanday.VisualStudio.AddIn for embedded resources. Workaround...
                    Stream fileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("NPanday.VisualStudio.log4net.xml");
                    if (fileStream == null)
                    {
                        fileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("NPanday.VisualStudio.Addin.log4net.xml");
                    }
                    if (fileStream != null)
                    {
                        XmlConfigurator.Configure(fileStream);
                    }
                    else
                    {
                        outputWindowPane.OutputString("Failed to configure logging subsystem: No log4net.xml configuration found\n");
                    }
                }
                catch (Exception e)
                {
                    outputWindowPane.OutputString("Failed to configure logging subsystem: " + e.Message + "\n");
                }

                Level level =  Level.Info;
                // TODO: should be a better way to enable this
                if (_addInInstance.Name.Contains("SNAPSHOT"))
                {
                    level = Level.Debug;
                }

                Hierarchy h = (Hierarchy)LogManager.GetRepository();
                Logger rootLogger = h.Root;
                OutputWindowPaneAppender appender = new OutputWindowPaneAppender(outputWindowPane, level);
                rootLogger.AddAppender(appender);

                log.Debug("Intialized panes");

                _buttonCommandRegistry = new ButtonCommandRegistry(_applicationObject, buildCommandContext);

                _finder = new VisualStudioControlsFinder(_applicationObject);
                _finder.IndexCommands();

                globalSolutionEvents = (EnvDTE.SolutionEvents)((Events2)_applicationObject.Events).SolutionEvents;
                globalSolutionEvents.BeforeClosing += new _dispSolutionEvents_BeforeClosingEventHandler(SolutionEvents_BeforeClosing);
                globalSolutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SolutionEvents_Opened);
                globalSolutionEvents.ProjectAdded += new _dispSolutionEvents_ProjectAddedEventHandler(SolutionEvents_ProjectAdded);

                projectItemsEvents = (EnvDTE.ProjectItemsEvents)((Events2)_applicationObject.Events).ProjectItemsEvents;
                projectItemsEvents.ItemAdded += new _dispProjectItemsEvents_ItemAddedEventHandler(ProjectItemEvents_ItemAdded);
                projectItemsEvents.ItemRemoved += new _dispProjectItemsEvents_ItemRemovedEventHandler(ProjectItemEvents_ItemRemoved);
                projectItemsEvents.ItemRenamed += new _dispProjectItemsEvents_ItemRenamedEventHandler(ProjectItemEvents_ItemRenamed);
            }

            log.Debug("OnConnection() called with connect mode " + connectMode);

            // this is only called once per installation
            if (connectMode == ext_ConnectMode.ext_cm_UISetup)
            {
                log.Debug("Registering Startup-Command in Tools-Menu");

                Command toolsMenuCommand = null;
                object[] contextGUIDS = new object[] { };
                Commands2 commands = (Commands2)_applicationObject.Commands;

                string toolsMenuName = VSCommandCaptions.Tools;

                //Place the command on the tools menu.
                //Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:
                CommandBar menuBarCommandBar = ((CommandBars)_applicationObject.CommandBars)["MenuBar"];

                //Find the Tools command bar on the MenuBar command bar:
                CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
                CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;

                if (toolsPopup == null)
                {
                    string message = "Will skip adding control, as the tools popup could not be found with name '" + toolsMenuName + "'";
                    log.Warn(message);
                    MessageBox.Show(message);
                }

                //This try/catch block can be duplicated if you wish to add multiple commands to be handled by your Add-in,
                //  just make sure you also update the QueryStatus/Exec method to include the new command names.
                try
                {
                    //Add a command to the Commands collection:
                    toolsMenuCommand = commands.AddNamedCommand2(_addInInstance, "NPandayAddin",
                        Messages.MSG_D_NPANDAY_BUILD_SYSTEM, Messages.MSG_T_NPANDAY_BUILDSYSTEM, true, 480, ref contextGUIDS,
                        (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled,
                        (int)vsCommandStyle.vsCommandStylePictAndText,
                        vsCommandControlType.vsCommandControlTypeButton);

                    //Add a control for the command to the tools menu:
                    if ((toolsMenuCommand != null) && (toolsPopup != null))
                    {
                        toolsMenuCommand.AddControl(toolsPopup.CommandBar, 1);
                    }
                    else
                    {
                        string message = "Skipped adding control as the NPanday start command could not be found.";
                        log.Warn(message);
                        MessageBox.Show(message);
                    }
                }
                catch (System.ArgumentException ex)
                {
                    //If we are here, then the exception is probably because a command with that name
                    //  already exists. If so there is no need to recreate the command and we can
                    //  safely ignore the exception.
                    log.Warn("Exception occured when adding NPanday to the Tools menu: " + ex.Message, ex);
                }

            }


            if (connectMode == ext_ConnectMode.ext_cm_AfterStartup)
            {
                launchNPandayBuildSystem();
            }
        }

        private OutputWindowPane getOrCreateOutputPane(string paneName)
        {
            Window win = _applicationObject.Windows.Item(EnvDTE.Constants.vsWindowKindOutput);
            OutputWindow outputWindow = (OutputWindow)win.Object;
            OutputWindowPanes panes = outputWindow.OutputWindowPanes;

            foreach (OutputWindowPane outputPane in panes)
            {
                if (outputPane.Name == paneName)
                {
                    return outputPane;
                }
            }
            return outputWindow.OutputWindowPanes.Add(paneName);
        }

        private IButtonCommandContext buildCommandContext()
        {
            return new ButtonCommandContext(this, _applicationObject);
        }

        private class ButtonCommandContext : IButtonCommandContext
        {
            private Connect _connect;
            private readonly DTE2 _application;

            public ButtonCommandContext(Connect connect, DTE2 application)
            {
                _connect = connect;
                _application = application;
            }

            public FileInfo CurrentSelectedProjectPom
            {
                get { return _connect.CurrentSelectedProjectPom; }
            }

            public ArtifactContext ArtifactContext
            {
                get { return _connect.container; }
            }

            public void ExecuteCommand(string visualStudioCommandName)
            {
                _application.ExecuteCommand(visualStudioCommandName, "");
            }

            public void ExecuteCommand<TCommand>() where TCommand : ButtonCommand, new()
            {
                _connect._buttonCommandRegistry.Excecute<TCommand>(this);
            }
        }

        void mavenRunner_RunnerStopped(object sender, EventArgs e)
        {
            //stopButton.Enabled = false;
        }

        public static bool IsWebSite(Project project)
        {
            return project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.Web_Site), StringComparison.OrdinalIgnoreCase);
        }

        public static bool IsWebProject(Project project)
        {
            // quick check for the right extender, in lieu of full GUID availability (see below)
            try
            {
                if (project.get_Extender("WebApplication") != null)
                {
                    return true;
                }
            }
            catch
            {
                // ignore
            }

            // project Kind is only the main one, so the below is not often correct. A more definitive query would be to get all the subtype GUIDs for a project (see http://www.mztools.com/articles/2007/mz2007016.aspx)
            return project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.Web_Site), StringComparison.OrdinalIgnoreCase) ||
                project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.Web_Application), StringComparison.OrdinalIgnoreCase);
        }

        public static bool IsCloudProject(Project project)
        {
            return project.Kind.Equals(VisualStudioProjectType.GetVisualStudioProjectTypeGuid(VisualStudioProjectTypeEnum.WindowsAzure_CloudService), StringComparison.OrdinalIgnoreCase);
        }

        private const string FOLDER_KIND_GUID = "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}";

        public static bool IsFolder(Project project)
        {
            // make sure there's a project item
            if (project == null)
                return false;

            // compare the project kind to the folder guid
            return (String.Compare(project.Kind, FOLDER_KIND_GUID, true) == 0);
        }

        void InsertKeyTag(string filePath, string key)
        {
            try
            {
                StreamReader reader = new StreamReader(filePath);
                List<String> contents = new List<string>();

                string temp = reader.ReadLine();
                contents.Add(temp);

                while (temp != null)
                {
                    temp = reader.ReadLine();
                    if (temp != null)
                    {
                        contents.Add(temp);
                        string includeSources = "</includeSources>";
                        if (temp.Contains(includeSources))
                        {
                            string keyNode = temp.Substring(0, temp.IndexOf(includeSources));
                            keyNode += string.Format("<keyfile>{0}</keyfile>", key);
                            contents.Add(keyNode);
                        }
                    }
                }
                reader.Close();

                TextWriter writer = new StreamWriter(filePath);

                foreach (string item in contents)
                {
                    writer.WriteLine(item);
                }

                writer.Close();
            }
            catch (Exception e)
            {
                log.Error(e.Message);
            }


        }

        void SigningEvents_SignatureAdded()
        {
            Solution2 solution = (Solution2)_applicationObject.Solution;
            string pomFilePath = string.Empty;
            foreach (Project project in solution.Projects)
            {
                string name = null;
                try
                {
                    name = project.FullName;
                }
                catch
                {
                    // ignore - project is not yet set up
                }
                if (string.IsNullOrEmpty(name))
                    continue;

                String key = string.Empty;
                XmlDocument doc = new XmlDocument();
                bool isSigned = false;
                try
                {
                    //Reading the project file
                    doc.Load(project.FullName);

                    //construct the path for the pom file and check for file existance.
                    //return if file does not exist.
                    pomFilePath = project.FullName.Substring(0, project.FullName.LastIndexOf("\\"));
                    pomFilePath += "\\pom.xml";
                    if (!File.Exists(pomFilePath))
                    {
                        return;
                    }
                    XmlNodeList keyFileList = doc.GetElementsByTagName("AssemblyOriginatorKeyFile");
                    XmlNodeList signBoolList = doc.GetElementsByTagName("SignAssembly");

                    foreach (XmlNode item in signBoolList)
                    {
                        if (item.InnerText.Equals("true"))
                        {
                            isSigned = true;
                        }
                    }

                    if (isSigned)
                    {
                        foreach (XmlNode item in keyFileList)
{'fraction_non_alphanumeric': 0.08039105359403573, 'fraction_numerical': 0.0033056688704459136, 'mean_word_length': 1.6397307960083547, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 28}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7060908', 'n_tokens_mistral': 13754, 'n_tokens_neox': 12774, 'n_words': 3009}