/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
*/
/************************************************************************************
* Copyright (c) 2014  All Rights Reserved. 
* CLR版本： 4.0.30319.35317 
* 创建人：  luoshupeng 
* 电子邮箱：luoshupeng@outlook.com 
* 创建时间：8/28/2014 6:30:57 PM 
* 描述： 项目转换
* ===================================================================== 
* 修改标记  
* 修改时间：8/28/2014 6:30:57 PM 
* 修改人： luoshupeng 
* 描述：   
************************************************************************************/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace VSConverterLib
{
    public class ProjectConverter : IConverter
    {        
        /// <summary>
        /// 代表命名空间的文件使用的项目。
        /// </summary>
        private readonly string namespaceName = "http://schemas.microsoft.com/developer/msbuild/2003";

        /// <summary>
        /// 对项目文件的路径。
        /// </summary>
        private string projectPath;

        /// <summary>
        /// “ToolsVersion属性。
        /// </summary>
        private XAttribute toolsVersion;

        /// <summary>
        /// 'ProductVersion' 元素.
        /// </summary>
        private XElement productVersion;

        /// <summary>
        /// 'TargetFrameworkVersion' 元素.
        /// </summary>
        private XElement targetFrameworkVersion;

        /// <summary>
        /// 'Import' 元素's 'Project' 属性.
        /// </summary>
        private XAttribute importProject;

        /// <summary>
        /// 一个XML对象包含了项目的内容。
        /// </summary>
        private XDocument project;

        /// <summary>
        /// 当前项目的版本。
        /// </summary>
        private VisualStudioVersion vsVersion;

        private bool IsReady;

        public ProjectConverter()
        {
            //Initialize class
        }

        public ProjectConverter(string projectPath)
        {
            this.Load(projectPath);
        }

        public string SolutionName
        {
            get;
            private set;
        }

        public VisualStudioVersion VisualStudioVersion
        {
            get { return this.vsVersion; }
        }

        public IdeVersion IDEVersion
        {
            get;
            private set;
        }

        public bool SaveAs
        {
            get;
            set;
        }

        public bool Load(string projectPath)
        {
            if (string.IsNullOrEmpty(projectPath))
            {
                throw new ArgumentNullException("projectPath can`t be null or empty.");
            }

            this.projectPath = projectPath;

            try
            {
                project = XDocument.Load(this.projectPath);
            }
            catch (Exception)
            {
                project = null;
            }

            bool ret = false;
            this.vsVersion = VisualStudioVersion.Unknown;
            this.IsReady = false;
            if (this.project != null)
            {
                this.toolsVersion = this.project.Root.Attribute(XName.Get("ToolsVersion"));
                this.productVersion = this.project.Root.Element(XName.Get("PropertyGroup", this.namespaceName)).Element(XName.Get("ProductVersion", this.namespaceName));
                this.targetFrameworkVersion = this.project.Root.Element(XName.Get("PropertyGroup", this.namespaceName)).Element(XName.Get("TargetFrameworkVersion", this.namespaceName));
                this.importProject = this.project.Root.Element(XName.Get("Import", this.namespaceName)).Attribute(XName.Get("Project"));
                this.SolutionName = Path.GetFileNameWithoutExtension(this.projectPath);

                this.vsVersion = this.GetVsVersion();
                this.IsReady = true;
                ret = true;
            }

            return ret;
        }
        
        public ConversionResult ConvertTo(VisualStudioVersion VSVersion)
        {
            this.IDEVersion = IdeVersion.Default;

            ConversionResult ret = new ConversionResult();
            ret.ConverterReference = this;
            if (!this.IsReady)
            {
                ret.ConversionStatus = ConversionStatus.NotReady;
                return ret;
            }

            // Calling the relevant method for editing the attributes and elements of the XML.
            switch (VSVersion)
            {
                case VisualStudioVersion.VisualStudio2002:
                    this.ConvertToVs2002(VSVersion);
                    break;
                case VisualStudioVersion.VisualStudio2003:
                    this.ConvertToVs2003(VSVersion);
                    break;
                case VisualStudioVersion.VisualStudio2005:
                    this.ConvertToVs2005(VSVersion);
                    break;
                case VisualStudioVersion.VisualStudio2008:
                    this.ConvertToVs2008(VSVersion);
                    break;
                case VisualStudioVersion.VisualStudio2010:
                    this.ConvertToVs2010(VSVersion);
                    break;
                case VisualStudioVersion.VisualStudio2012:
                    this.ConvertToVs2012(VSVersion);
                    break;
                case VisualStudioVersion.VisualStudio2013:
                    this.ConvertToVs2013(VSVersion);
                    break;
            }

            // Saving the Project XML file.
            FileStream fileStream = null;
            XmlTextWriter writer = null;
            ret.ConverterReference = this;
            try
            {
                string newPath = this.projectPath;
                if (this.SaveAs)
                {
                    string extensionName = Path.GetExtension(newPath);
                    newPath = newPath.Replace(extensionName, ((int)VSVersion).ToString() + extensionName);
                }
                fileStream = new FileStream(newPath, FileMode.Create);
                writer = new XmlTextWriter(fileStream, Encoding.UTF8);
                writer.Formatting = Formatting.Indented;
                this.project.WriteTo(writer);
                ret.ConversionStatus = ConversionStatus.Succeeded;
            }
            catch (Exception)
            {
                ret.ConversionStatus = ConversionStatus.Failed;
            }
            finally
            {
                this.IsReady = false;
                if (writer != null)
                {
                    writer.Close();
                }
            }

            return ret;
        }

        public ConversionResult ConvertTo(VisualStudioVersion VSVersion, IdeVersion IDEVersion)
        {
            return this.ConvertTo(VSVersion, IdeVersion.Default);
        }

        private VisualStudioVersion GetVsVersion()
        {
            VisualStudioVersion vsVer = VisualStudioVersion.Unknown;

            double product;
            if (this.productVersion != null)
            {
                if (Double.TryParse(this.productVersion.Value, out product))
                {
                    if (product == 7)
                    {
                        vsVer = VisualStudioVersion.VisualStudio2002;
                    }
                    else if (product == 7.1)
                    {
                        vsVer = VisualStudioVersion.VisualStudio2003;
                    }
                    else if (product == 8)
                    {
                        vsVer = VisualStudioVersion.VisualStudio2005;
                    }
                    else if (product == 9)
                    {
                        vsVer = VisualStudioVersion.VisualStudio2008;
                    }
                    else if (product == 10)
                    {
                        vsVer = VisualStudioVersion.VisualStudio2010;
                    }
                }
            }

            return vsVer;
        }

        private void ConvertToVs2002(VisualStudioVersion vsVersion)
        {
            if (this.toolsVersion != null)
            {
                this.toolsVersion.Remove();
            }

            if (this.targetFrameworkVersion != null)
            {
                this.targetFrameworkVersion.Remove();
            }

            if (this.productVersion != null)
            {
                this.productVersion.Value = "7.0.0000";
            }

            if (this.importProject != null && !this.importProject.Value.Contains("$(MSBuildBinPath)"))
            {
                this.importProject.Value = this.importProject.Value.Replace("$(MSBuildToolsPath)", "$(MSBuildBinPath)");
            }
        }

        private void ConvertToVs2003(VisualStudioVersion vsVersion)
        {
            if (this.toolsVersion != null)
            {
                this.toolsVersion.Remove();
            }

            if (this.targetFrameworkVersion != null)
            {
                this.targetFrameworkVersion.Remove();
            }

            if (this.productVersion != null)
            {
                this.productVersion.Value = "7.1.0000";
            }

            if (this.importProject != null && !this.importProject.Value.Contains("$(MSBuildBinPath)"))
            {
                this.importProject.Value = this.importProject.Value.Replace("$(MSBuildToolsPath)", "$(MSBuildBinPath)");
            }
        }

        private void ConvertToVs2005(VisualStudioVersion vsVersion)
        {
            if (this.toolsVersion != null)
            {
                this.toolsVersion.Remove();
            }

            if (this.targetFrameworkVersion != null)
            {
                this.targetFrameworkVersion.Remove();
            }

            if (this.productVersion != null)
            {
                this.productVersion.Value = "8.0.50727";
            }

            if (this.importProject != null && !this.importProject.Value.Contains("$(MSBuildBinPath)"))
            {
                this.importProject.Value = this.importProject.Value.Replace("$(MSBuildToolsPath)", "$(MSBuildBinPath)");
            }
        }

        private void ConvertToVs2008(VisualStudioVersion vsVersion)
        {
            if (this.toolsVersion != null)
            {
                double tools;
                if (Double.TryParse(this.toolsVersion.Value, out tools) && tools != 3.5)
                {
                    this.toolsVersion.Value = "3.5";
                }
            }
            else
            {
                this.project.Root.Add(new XAttribute(XName.Get("ToolsVersion"), "3.5"));
            }

            if (this.productVersion != null)
            {
                this.productVersion.Value = "9.0.21022";
            }

            if (this.targetFrameworkVersion != null)
            {
                double framework;
                if (Double.TryParse(
                    this.targetFrameworkVersion.Value.Substring(1, this.targetFrameworkVersion.Value.Length - 2),
                    out framework) &&
                    framework > 3.5)
                {
                    this.targetFrameworkVersion.Value = "v3.5";
                }
            }
            else
            {
                XElement newToolsVersion = new XElement(XName.Get("TargetFrameworkVersion", this.namespaceName), "v2.0");
                this.project.Root.Element(XName.Get("PropertyGroup", this.namespaceName)).Add(newToolsVersion);
            }

            if (this.importProject != null && !this.importProject.Value.Contains("$(MSBuildToolsPath)"))
            {
                this.importProject.Value = this.importProject.Value.Replace("$(MSBuildBinPath)", "$(MSBuildToolsPath)");
            }
        }

        private void ConvertToVs2010(VisualStudioVersion vsVersion)
        {
            if (this.toolsVersion != null)
            {
                double tools;
                if (Double.TryParse(this.toolsVersion.Value, out tools) && tools != 4.0)
                {
                    this.toolsVersion.Value = "4.0";
                }
            }
            else
            {
                this.project.Root.Add(new XAttribute(XName.Get("ToolsVersion"), "4.0"));
            }

            if (this.productVersion != null)
            {
                this.productVersion.Value = "10.0.30319.1";
            }

            if (this.targetFrameworkVersion == null)
            {
                XElement newToolsVersion = new XElement(XName.Get("TargetFrameworkVersion", this.namespaceName), "v2.0");
                this.project.Root.Element(XName.Get("PropertyGroup", this.namespaceName)).Add(newToolsVersion);
            }

            if (this.importProject != null && !this.importProject.Value.Contains("$(MSBuildToolsPath)"))
            {
                this.importProject.Value = this.importProject.Value.Replace("$(MSBuildBinPath)", "$(MSBuildToolsPath)");
            }
        }

        private void ConvertToVs2012(VisualStudioVersion vsVersion)
        {
            if (this.toolsVersion != null)
            {
                double tools;
                if (Double.TryParse(this.toolsVersion.Value, out tools) && tools != 4.0)
                {
                    this.toolsVersion.Value = "4.0";
                }
            }
            else
            {
                this.project.Root.Add(new XAttribute(XName.Get("ToolsVersion"), "4.0"));
            }

            if (this.targetFrameworkVersion == null)
            {
                XElement newToolsVersion = new XElement(XName.Get("TargetFrameworkVersion", this.namespaceName), "v2.0");
                this.project.Root.Element(XName.Get("PropertyGroup", this.namespaceName)).Add(newToolsVersion);
            }

            if (this.importProject != null && !this.importProject.Value.Contains("$(MSBuildToolsPath)"))
            {
                this.importProject.Value = this.importProject.Value.Replace("$(MSBuildBinPath)", "$(MSBuildToolsPath)");
            }
        }

        private void ConvertToVs2013(VisualStudioVersion vsVersion)
        {
            if (this.toolsVersion != null)
            {
                double tools;
                if (Double.TryParse(this.toolsVersion.Value, out tools) && tools != 12.0)
                {
                    this.toolsVersion.Value = "12.0";
                }
            }
            else
            {
                this.project.Root.Add(new XAttribute(XName.Get("ToolsVersion"), "12.0"));
            }

            if (this.targetFrameworkVersion == null)
            {
                XElement newToolsVersion = new XElement(XName.Get("TargetFrameworkVersion", this.namespaceName), "v2.0");
                this.project.Root.Element(XName.Get("PropertyGroup", this.namespaceName)).Add(newToolsVersion);
            }

            if (this.importProject != null && !this.importProject.Value.Contains("$(MSBuildToolsPath)"))
            {
                this.importProject.Value = this.importProject.Value.Replace("$(MSBuildBinPath)", "$(MSBuildToolsPath)");
            }
        }
    }
}
