//===============================================================================
// 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.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using Config = System.Configuration.Configuration;
using ConfigManager = System.Configuration.ConfigurationManager;
using System.IO;
using System.Web.Configuration;
using System.Configuration;
using System.ComponentModel;
using GatLib = Microsoft.Practices.RecipeFramework.Library.Configuration.Actions;
using System.Xml;
using Microsoft.Practices.RecipeFramework.Library;
using System.Web.Caching;
using System.Web;
using Microsoft.Practices.RecipeFramework.Extensions.Actions.Templates;
using Microsoft.Practices.RecipeFramework.Extensions.CommonHelpers;
using Microsoft.Practices.Common.Services;
using System.ComponentModel.Design;
using Microsoft.Practices.ComponentModel;
using Microsoft.Practices.RecipeFramework;

namespace Microsoft.Practices.SoftwareFactories.ServiceFactory.Actions.Configuration.Helpers
{
    /// <summary>
    /// Helper class for handling file config stuff.
    /// </summary>
    public static class ConfigurationVSHelper
    {
        #region Constants

        private const string AppConfigFileName = "App.config";
        private const string WebConfigFileName = "Web.config";
        //private const string WebProjectKind = VsWebSite.PrjKind.prjKindVenusProject;
        private const string DtePropertiesBrowseURL = "BrowseURL";
        private const string DtePropertiesFullPath = "FullPath";

        #endregion

        #region Cache objects

        private static Cache configurationCache = HttpRuntime.Cache;

        class CachedItem
        {
            private Config configuration;
            public CachedItem(Config configuration)
            {
                this.configuration = configuration;
            }

            public Config Configuration
            {
                get { return configuration; }
            }
        }

        #endregion

        /// <summary>
        /// Gets the configuration.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <returns></returns>
        public static System.Configuration.Configuration GetConfiguration(Project project)
        {
            if (project == null ||
                string.IsNullOrEmpty(project.FullName))
            {
                return null;
            }

            CachedItem cachedItem = configurationCache.Get(project.FullName) as CachedItem;

            if (cachedItem == null)
            {
                ProjectItem configurationItem = ConfigurationVSHelper.GetConfigurationItem(project);
                string dependencyFile;
                if (configurationItem == null)
                {
                    //The configuration file does not exists in the project
                    cachedItem = new CachedItem(null);
                    dependencyFile = project.FullName;
                }
                else
                {
                    if (DteHelper.IsWebProject(project))
                    {
                        cachedItem = new CachedItem(ConfigurationVSHelper.OpenWebConfiguration(project));
                    }
                    else
                    {
                        cachedItem = new CachedItem(ConfigurationVSHelper.OpenExeConfiguration(configurationItem.get_FileNames(0)));
                    }
                    dependencyFile = cachedItem.Configuration.FilePath;
                }
                configurationCache.Insert(project.FullName, cachedItem, new CacheDependency(dependencyFile));
            }

            return cachedItem.Configuration;
        }

        /// <summary>
        /// Gets the configuration item.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <returns></returns>
        public static ProjectItem GetConfigurationItem(Project project)
        {
            if (project == null)
            {
                return null;
            }

            ProjectItem projectItem = null;

            foreach (ProjectItem item in project.ProjectItems)
            {
                if (item.Name.Equals(AppConfigFileName, StringComparison.InvariantCultureIgnoreCase) ||
                    item.Name.Equals(WebConfigFileName, StringComparison.InvariantCultureIgnoreCase))
                {
                    projectItem = item;
                    break;
                }
            }

            return projectItem;
        }

        /// <summary>
        /// Adds the new configuration.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="action">The action.</param>
        /// <returns></returns>
        public static System.Configuration.Configuration AddNewConfiguration(
            Project project,
            IContainer action)
        {
            Guard.ArgumentNotNull(project, "project");
            Guard.ArgumentNotNull(action, "action");
            
            if (DteHelper.IsWebProject(project))
            {
                using (GetWebConfigurationAction configAction = new GetWebConfigurationAction())
                {
                    action.Add(configAction);
                    configAction.Project = project;
                    configAction.Execute();
                    return configAction.Configuration;
                }
            }

            using (GatLib.GetConfigurationAction configAction = new GatLib.GetConfigurationAction())
            {
                action.Add(configAction);
                configAction.Project = project;
                configAction.Execute();
                return configAction.Configuration;
            }
        }

        /// <summary>
        /// Opens a new temp configuration file.
        /// </summary>
        /// <returns></returns>
        public static System.Configuration.Configuration OpenExeConfiguration()
        {
            string tempFile = Path.GetTempFileName();

            try
            {
                //Create an empty configuration document
                using (XmlTextWriter writer = new XmlTextWriter(tempFile, new UTF8Encoding(true, true)))
                {
                    writer.WriteStartDocument();
                    writer.WriteStartElement("configuration");
                    writer.WriteEndDocument();
                    writer.Flush();
                }
            }
            catch
            {
                if(File.Exists(tempFile))
                {
                    File.Delete(tempFile);
                }
                throw;
            }

            return OpenExeConfiguration(tempFile);
        }

        /// <summary>
        /// Opens the exe configuration.
        /// </summary>
        /// <param name="configurationFile">The configuration file.</param>
        /// <returns></returns>
        public static System.Configuration.Configuration OpenExeConfiguration(string configurationFile)
        {
            FileInfo fileInfo = new FileInfo(configurationFile);
            ExeConfigurationFileMap exeFileMap = new ExeConfigurationFileMap();
            exeFileMap.ExeConfigFilename = fileInfo.FullName;

            return System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(
                exeFileMap, ConfigurationUserLevel.None);
        }

        /// <summary>
        /// Opens the web configuration.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <returns></returns>
        public static System.Configuration.Configuration OpenWebConfiguration(Project project)
        {
            Guard.ArgumentNotNull(project, "project");
            if (!DteHelper.IsWebProject(project))
            {
                throw new ArgumentOutOfRangeException("project");
            }

            string virtualPath = project.Properties.Item(DtePropertiesBrowseURL).Value.ToString();
            string physicalPath = project.Properties.Item(DtePropertiesFullPath).Value.ToString();

            return OpenWebConfiguration(virtualPath, physicalPath);
        }

        /// <summary>
        /// Opens the web configuration.
        /// </summary>
        /// <param name="configurationFileVirtualPath">The configuration file virtual path.</param>
        /// <param name="configurationFilePhysicalPath">The configuration file physical path.</param>
        /// <returns></returns>
        public static System.Configuration.Configuration OpenWebConfiguration(
            string configurationFileVirtualPath, string configurationFilePhysicalPath)
        {
            WebConfigurationFileMap fileMap = new WebConfigurationFileMap();
            Uri uri = new Uri(configurationFileVirtualPath);
            fileMap.VirtualDirectories.Add(uri.LocalPath,
                    new VirtualDirectoryMapping(configurationFilePhysicalPath, true));

            return WebConfigurationManager.OpenMappedWebConfiguration(fileMap, uri.LocalPath);
        }
         
        /// <summary>
        /// Gets the configuration file path.
        /// </summary>
        /// <param name="solution">The solution.</param>
        /// <param name="fileName">The filename.</param>
        /// <returns></returns>
        public static string GetConfigurationFilePath(Solution solution, string fileName)
        {
            Guard.ArgumentNotNull(solution, "solution");
            Guard.ArgumentNotNullOrEmptyString(fileName, "fileName");

            return Path.Combine(
                Path.GetDirectoryName(solution.FileName), fileName);
        }
    }
}
