//===============================================================================
// Microsoft patterns & practices
// Web Service Software Factory
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.RecipeFramework;
using EnvDTE;
using Microsoft.VisualStudio.TextManager.Interop;
#endregion

namespace Microsoft.Practices.SoftwareFactories.ServiceFactory.Actions
{
    /// <summary>
    /// Action that implement interface members
    /// </summary>
    public class ImplementInterfaceAction : ConfigurableAction
    {
        #region ConfigurableAction Implementation
        /// <summary>
        /// See <see cref="M:Microsoft.Practices.RecipeFramework.IAction.Execute"/>.
        /// </summary>
        public override void Execute()
        {
            DTE vs = (DTE)GetService(typeof(DTE));

            ProjectItem item = vs.ActiveDocument.ProjectItem;

            if(item != null && item.FileCodeModel != null)
            {
                foreach(CodeElement element in item.FileCodeModel.CodeElements)
                {
                    if(element.Kind == vsCMElement.vsCMElementNamespace)
                    {
                        foreach(CodeElement type in ((CodeNamespace)element).Members)
                        {
                            CodeClass codeClass = type as CodeClass;
                            if (codeClass != null &&
                                HasInterfacesToImplement(codeClass))
                            {
                                string classDeclaration = GetClassDeclarationLine(codeClass);
                                List<int> columnNumbers = GetInterfacesStartColumns(classDeclaration, 4);

                                TextPoint textPoint = codeClass.GetStartPoint(vsCMPart.vsCMPartHeader);
                                IVsTextManager textManager = (IVsTextManager)GetService(typeof(SVsTextManager));
                                IVsTextBuffer buffer = null;
                                IVsTextView view;

                                textManager.GetActiveView(1, buffer, out view);

                                foreach(int column in columnNumbers)
                                {
                                    view.SetCaretPos(textPoint.Line - 1, column);
                                    vs.ExecuteCommand("Edit.ImplementInterfaceStubsImplicitly", "");
                                }

                                item.Save(item.Name);
                                return;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// See <see cref="M:Microsoft.Practices.RecipeFramework.IAction.Undo"/>.
        /// </summary>
        public override void Undo()
        {
            //Not Implemented
        }
        #endregion

        #region Private Implementation
        /// <summary>
        /// Gets the class declaration line.
        /// </summary>
        /// <param name="codeClass">The code class.</param>
        /// <returns></returns>
        private static string GetClassDeclarationLine(CodeClass codeClass)
        {
            EditPoint point = codeClass.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint();
            string classDeclaration = point.GetText(codeClass.GetStartPoint(vsCMPart.vsCMPartHeader).LineLength);

            return classDeclaration;
        }

        /// <summary>
        /// Gets the interfaces start columns.
        /// </summary>
        /// <param name="classDeclaration">The class declaration.</param>
        /// <param name="tabSize">Size of the tab.</param>
        /// <returns></returns>
        private static List<int> GetInterfacesStartColumns(string classDeclaration, int tabSize)
        {
            //"public partial class EmployeeManager : IEmployeeManager, IEmployeeManager1\r\n   "

            List<int> columnNumbers = new List<int>();
            int beginInterfaceColumn = classDeclaration.IndexOf(" : ") + 3;
            string temp = classDeclaration.Substring(beginInterfaceColumn);
            string[] interfaces = temp.Split(',');

            foreach(string interfaceName in interfaces)
            {
                columnNumbers.Add(classDeclaration.IndexOf(interfaceName.Trim(), beginInterfaceColumn) + 1 + tabSize);
            }

            return columnNumbers;
        }

        /// <summary>
        /// Checks if the class has interfaces with members to implement
        /// </summary>
        /// <param name="codeClass">The class</param>
        /// <returns>true if the class has at least one implemented interface with at least one method</returns>
        private static bool HasInterfacesToImplement(CodeClass codeClass)
        {
            foreach (CodeInterface codeInterface in codeClass.ImplementedInterfaces)
            {
                if (codeInterface.Members.Count > 0)
                    return true;
            }

            return false;
        }

        #endregion
    }
}
