<|start_filename|>Mutatio/Commands.cs<|end_filename|>
﻿namespace Mutatio
{
    public enum Commands
    {
        ConvertProjectToNetStandard
    }
}

<|start_filename|>Mutatio/ConvertProjectToNetStandardHandler.cs<|end_filename|>
﻿using System;
using System.IO;
using System.Text;
using MonoDevelop.Components.Commands;
using MonoDevelop.Ide;
using MonoDevelop.Projects;
using Mutatio.Extensions;

namespace Mutatio
{
    public class ConvertProjectToNetStandardHandler : CommandHandler
    {
        ProjectOperations ProjectOperations => IdeApp.ProjectOperations;

        protected override void Update(CommandInfo info)
        {
            info.Enabled =
                IsWorkspaceOpen()
                && ProjectIsNotBuildingOrRunning()
                && UserSelectedItemIsProject()
                && UserSelectedProjectIsPortableLibrary()
                && UserSelectedProjectIsCSharpProject()
                && UserSelectedProjectIsNotNetStandard20();
        }

        protected async override void Run()
        {
            using (var monitor = IdeApp.Workbench.ProgressMonitors.GetToolOutputProgressMonitor(false))
            {
                monitor.BeginTask(1);

                try
                {
                    // We don't want to miss any log messages after reloading the solution
                    var logSummary = new StringBuilder();
                    var proj = ProjectOperations.CurrentSelectedItem as DotNetProject;

                    logSummary.AppendLine($"Converting {proj.Name}{proj.GetProjFileExtension()} to NET Standard 2.0 format ..");

                    var projFilePath = proj.GetProjFilePath();

                    logSummary.AppendLine($"Generating new {proj.GetProjFileExtension()}");

                    var projectTemplate = ProjectTemplateFactory.GetTemplate(proj);
                    logSummary.AppendLine($"Project template detected: {projectTemplate}");
                    var netStandardProjContent = new NetStandardProjFileGenerator().GenerateProjForNetStandard(projectTemplate.GetPackages);

                    logSummary.AppendLine("Creating backup");

                    projectTemplate.BackupOldFormatFiles();

                    logSummary.AppendLine("Deleting old files");

                    projectTemplate.CleanUpOldFormatFiles();

                    // Create a new .xproj
                    File.WriteAllText($"{projFilePath}", netStandardProjContent, Encoding.UTF8);

                    // TODO: Programmatically reload the project instead of re-opening.
                    logSummary.AppendLine("Re-opening the project");

                    await IdeApp.Workspace.Close(true);
                    await IdeApp.Workspace.OpenWorkspaceItem(proj.ParentSolution.FileName);

                    monitor.Log.WriteLine(logSummary);

                    monitor.Log.WriteLine("Please note that .NET Standard 2.0 is supported only from Xamarin.Forms 2.4+.");
                    monitor.Log.WriteLine("More information can be found here: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/internals/net-standard");
                    monitor.ReportSuccess("Convertion succeed.");
                }
                catch (Exception ex)
                {
                    monitor.ReportError($"Convertion failed. Please create an issue on github: {Consts.ProjectUrl}", ex);
                }
                finally
                {
                    monitor.EndTask();
                }
            }
        }

        // Shoud be enabled only when the workspace is opened
        bool IsWorkspaceOpen() => IdeApp.Workspace.IsOpen;

        bool ProjectIsNotBuildingOrRunning()
        {
            var isBuild = ProjectOperations.IsBuilding(ProjectOperations.CurrentSelectedSolution);
            var isRun = ProjectOperations.IsRunning(ProjectOperations.CurrentSelectedSolution);

            return !isBuild && !isRun && IdeApp.ProjectOperations.CurrentBuildOperation.IsCompleted
                         && IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted;
        }

        bool UserSelectedItemIsProject() => ProjectOperations.CurrentSelectedItem is DotNetProject;
        bool UserSelectedProjectIsCSharpProject() => (ProjectOperations.CurrentSelectedItem as DotNetProject).IsCSharpProject();
        bool UserSelectedProjectIsNotNetStandard20() => !(ProjectOperations.CurrentSelectedItem as DotNetProject).TargetFramework.Name.Equals(".NETStandard 2.0", StringComparison.OrdinalIgnoreCase);
        bool UserSelectedProjectIsPortableLibrary() => (ProjectOperations.CurrentSelectedItem as DotNetProject).IsPortableLibrary;
    }
}

<|start_filename|>Mutatio/ProjectTemplates/ProjectWithPackagesJson.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using MonoDevelop.Core;
using MonoDevelop.Projects;
using Mutatio.Extensions;

namespace Mutatio
{
    public class ProjectWithPackagesJson : IProjectTemplate
    {
        readonly DotNetProject proj;

        public ProjectWithPackagesJson(DotNetProject proj)
        {
            this.proj = proj;
        }

        public void BackupOldFormatFiles()
        {
            var backupFolderPath = $"{proj.BaseDirectory.FullPath.ParentDirectory}/mutatio_backup";
            // Create backup directory
            FileService.CreateDirectory(backupFolderPath);
            // Backup current .xproj
            var projFilePath = proj.GetProjFilePath();
            FileService.CopyFile(projFilePath, $"{backupFolderPath}/{proj.Name}.{proj.GetProjFileExtension()}");
            // Backup packages.config
            var packagesConfigFilePath = proj.GetPackagesJsonFilePath();
            FileService.CopyFile(packagesConfigFilePath, $"{backupFolderPath}/packages.config");

            // Backup AssemblyInfo.x
            FileService.CopyDirectory(proj.GetPropertiesDirPath(), $"{backupFolderPath}/Properties");
        }

        public void CleanUpOldFormatFiles()
        {
            FileService.DeleteFile(proj.GetPackagesJsonFilePath());
            FileService.DeleteDirectory(proj.GetPropertiesDirPath());
            FileService.DeleteFile(proj.GetProjFilePath());
        }

        public IEnumerable<(string name, string version)> GetPackages()
        {
            var doc = XDocument.Load(proj.GetPackagesJsonFilePath());
            return doc.Elements("packages").Descendants().Select(p => (p.Attribute("id").Value, p.Attribute("version").Value));
        }

        public override string ToString() => "project with packages.json";
	}
}


<|start_filename|>Mutatio/ProjectTemplates/IProjectTemplate.cs<|end_filename|>
﻿using System.Collections.Generic;

namespace Mutatio
{
    public interface IProjectTemplate
    {
        void BackupOldFormatFiles();
        void CleanUpOldFormatFiles();
        IEnumerable<(string name, string version)> GetPackages();
    }
}


<|start_filename|>Mutatio/Extensions/DotNetProjectExtenions.cs<|end_filename|>
﻿using System;
using System.IO;
using MonoDevelop.Projects;

namespace Mutatio.Extensions
{
    public static class DotNetProjectExtenions
    {
        public static string GetPackagesJsonFilePath(this DotNetProject proj) => $"{proj.BaseDirectory.FullPath}/packages.config";
        public static string GetPropertiesDirPath(this DotNetProject proj) => $"{proj.BaseDirectory.FullPath}/Properties";

        public static string GetProjFilePath(this DotNetProject proj) => proj.FileName.FullPath;
        public static string GetProjFileExtension(this DotNetProject proj) => proj.FileName.Extension;

        public static string GetAssemblyInfoFilePath(this DotNetProject proj)
        {
            switch(proj.LanguageName)
            {
                case "C#":
                    return $"{proj.BaseDirectory.FullPath}/Properties/AssemblyInfo.cs";
                case "F#":
                    return $"{proj.BaseDirectory.FullPath}/AssemblyInfo.fs";
                case "VBNET":
                    return $"{proj.BaseDirectory.FullPath}/AssemblyInfo.vb";
                default:
                    throw new NotSupportedException($"{proj.LanguageName} is currently unsupported. Please create an issue on github: {Consts.ProjectUrl}");
            }
        }

        public static bool IsCSharpProject(this DotNetProject proj) => proj.LanguageName.Equals("c#", StringComparison.OrdinalIgnoreCase);

        public static string GetProjectJsonFilePath(this DotNetProject proj) => $"{proj.BaseDirectory.FullPath}/project.json";
        public static string GetProjectLockJsonFilePath(this DotNetProject proj) => $"{proj.BaseDirectory.FullPath}/project.lock.json";
        public static bool ContainsProjectJson(this DotNetProject proj) => File.Exists(GetProjectJsonFilePath(proj));
    }
}


<|start_filename|>Mutatio/ProjectTemplates/ProjectWithProjectJson.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using MonoDevelop.Core;
using MonoDevelop.Projects;
using Mutatio.Extensions;
using Newtonsoft.Json;

namespace Mutatio
{
    public class ProjectWithProjectJson : IProjectTemplate
    {
        readonly DotNetProject proj;

        public ProjectWithProjectJson(DotNetProject proj)
        {
            this.proj = proj;
        }

        public void BackupOldFormatFiles()
        {
            var backupFolderPath = $"{proj.BaseDirectory.FullPath.ParentDirectory}/mutatio_backup";
            // Create backup directory
            FileService.CreateDirectory(backupFolderPath);
            // Backup current .xproj
            var projFilePath = proj.GetProjFilePath();
            FileService.CopyFile(projFilePath, $"{backupFolderPath}/{proj.Name}.{proj.GetProjFileExtension()}");
            // Backup project.json
            var projectJsonFilePath = proj.GetProjectJsonFilePath();
            FileService.CopyFile(projectJsonFilePath, $"{backupFolderPath}/project.json");
            // Backup project.lock.json
            if(File.Exists(proj.GetProjectLockJsonFilePath()))
            {
                var projectLockJsonFilePath = proj.GetProjectLockJsonFilePath();
                FileService.CopyFile(projectLockJsonFilePath, $"{backupFolderPath}/project.lock.json");
            }
            // Backup AssemblyInfo.x
            FileService.CopyDirectory(proj.GetPropertiesDirPath(), $"{backupFolderPath}/Properties");
        }

        public void CleanUpOldFormatFiles()
        {
            FileService.DeleteFile(proj.GetProjectJsonFilePath());
            if (File.Exists(proj.GetProjectLockJsonFilePath()))
                FileService.DeleteFile(proj.GetProjectLockJsonFilePath());
            FileService.DeleteDirectory(proj.GetPropertiesDirPath());
            FileService.DeleteFile(proj.GetProjFilePath());
        }

        public IEnumerable<(string name, string version)> GetPackages()
        {
            var projJsonContents = File.ReadAllText(proj.GetProjectJsonFilePath(), new UTF8Encoding(true));
            var projJson = JsonConvert.DeserializeObject<ProjJson>(projJsonContents);
            return projJson.Dependencies.Select(d => (d.Key, d.Value));
        }

        public override string ToString() => "project with project.json";

        class ProjJson
        {
            [JsonProperty("dependencies")]
            public Dictionary<string, string> Dependencies { get; set; }
        }
	}
}


<|start_filename|>Mutatio/Consts.cs<|end_filename|>
﻿using System;
namespace Mutatio
{
    public static class Consts
    {
        public const string ProjectUrl = "https://github.com/yuv4ik/mutatio";
    }
}


<|start_filename|>Mutatio/NetStandardProjFileGenerator.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mutatio
{
    public class NetStandardProjFileGenerator
    {
        string netStandard20ProjFormat => @"<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    {0}
  </ItemGroup>

</Project>";
        
        public string GenerateProjForNetStandard(Func<IEnumerable<(string name, string version)>> getPackages)
        {
            var packages = getPackages();

            var packagesText = new StringBuilder(packages.Count());
            foreach (var package in packages)
                packagesText.AppendLine($"<PackageReference Include=\"{package.name}\" Version=\"{package.version}\"/>");

            return string.Format(netStandard20ProjFormat, packagesText);
        }
    }
}

<|start_filename|>Mutatio/ProjectTemplates/ProjectTemplateFactory.cs<|end_filename|>
﻿using MonoDevelop.Projects;
using Mutatio.Extensions;

namespace Mutatio
{
    public static class ProjectTemplateFactory
    {
        public static IProjectTemplate GetTemplate(DotNetProject project)
        {
            if (project.ContainsProjectJson())
                return new ProjectWithProjectJson(project);
            else
                return new ProjectWithPackagesJson(project);
        }
    }
}
