﻿using System;
using System.IO;
using System.Linq;
using Cake.Core;
using Cake.Core.Diagnostics;
using Cake.Core.IO;

namespace Cake.Common.Solution.Project.Properties
{
    /// <summary>
    /// The assembly info creator.
    /// </summary>
    public sealed class AssemblyInfoCreator
    {
        private readonly IFileSystem _fileSystem;
        private readonly ICakeEnvironment _environment;
        private readonly ICakeLog _log;

        /// <summary>
        /// Initializes a new instance of the <see cref="AssemblyInfoCreator"/> class.
        /// </summary>
        /// <param name="fileSystem">The file system.</param>
        /// <param name="environment">The environment.</param>
        /// <param name="log">The log.</param>
        public AssemblyInfoCreator(IFileSystem fileSystem, ICakeEnvironment environment, ICakeLog log)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException("fileSystem");
            }
            if (environment == null)
            {
                throw new ArgumentNullException("environment");
            }
            if (log == null)
            {
                throw new ArgumentNullException("log");
            }
            _fileSystem = fileSystem;
            _environment = environment;
            _log = log;
        }

        /// <summary>
        /// Creates an assembly info file.
        /// </summary>
        /// <param name="outputPath">The output path.</param>
        /// <param name="settings">The settings.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
        public void Create(FilePath outputPath, AssemblyInfoSettings settings)
        {
            if (outputPath == null)
            {
                throw new ArgumentNullException("outputPath");
            }
            if (settings == null)
            {
                throw new ArgumentNullException("settings");
            }

            // Create registrations.
            var registration = GetRegistrations(settings);

            // Get the absolute output path.
            var absoluteOutputPath = outputPath.MakeAbsolute(_environment);

            _log.Verbose("Creating assembly info file: {0}", absoluteOutputPath);

            using (var stream = _fileSystem.GetFile(absoluteOutputPath).OpenWrite())
            using (var writer = new StreamWriter(stream, System.Text.Encoding.UTF8))
            {
                // Write header.
                writer.WriteLine("//------------------------------------------------------------------------------");
                writer.WriteLine("// <auto-generated>");
                writer.WriteLine("//     This code was generated by Cake.");
                writer.WriteLine("// </auto-generated>");
                writer.WriteLine("//------------------------------------------------------------------------------");

                if (registration.Attributes.Count > 0)
                {
                    // Write namespaces.
                    var namespaces = registration.Namespaces.Select(n => string.Concat("using ", n, ";"));
                    foreach (var @namespace in namespaces)
                    {
                        writer.WriteLine(@namespace);
                    }

                    writer.WriteLine();

                    // Write attributes.
                    foreach (var attribute in registration.Attributes)
                    {
                        writer.WriteLine(string.Concat("[assembly: ", attribute.Key, "(", attribute.Value, ")]"));
                    }
                }
            }
        }

        private static AssemblyInfoRegistration GetRegistrations(AssemblyInfoSettings settings)
        {
            var registration = new AssemblyInfoRegistration();
            registration.AddString("AssemblyTitle", "System.Reflection", settings.Title);
            registration.AddString("AssemblyDescription", "System.Reflection", settings.Description);
            registration.AddString("AssemblyCompany", "System.Reflection", settings.Company);
            registration.AddString("AssemblyProduct", "System.Reflection", settings.Product);
            registration.AddString("AssemblyVersion", "System.Reflection", settings.Version);
            registration.AddString("AssemblyFileVersion", "System.Reflection", settings.FileVersion);
            registration.AddString("AssemblyInformationalVersion", "System.Reflection", settings.InformationalVersion);
            registration.AddString("AssemblyCopyright", "System.Reflection", settings.Copyright);
            registration.AddString("AssemblyTrademark", "System.Reflection", settings.Trademark);
            registration.AddString("Guid", "System.Runtime.InteropServices", settings.Guid);
            registration.AddBoolean("ComVisible", "System.Runtime.InteropServices", settings.ComVisible);
            registration.AddBoolean("CLSCompliant", "System", settings.CLSCompliant);
            return registration;
        }
    }
}
