﻿using Songhay.Models;
using System;
using System.IO;
using System.Reflection;
using System.Text;

namespace Songhay
{
    /// <summary>
    /// Static members related to <see cref="System.Reflection"/>.
    /// </summary>
    public static partial class ProgramAssemblyUtility
    {
        /// <summary>
        /// Returns a <see cref="string"/>
        /// about the executing assembly.
        /// </summary>
        /// <param name="targetAssembly">
        /// The executing <see cref="System.Reflection.Assembly"/>.
        /// </param>
        /// <returns>Returns <see cref="string"/></returns>
        public static string GetAssemblyInfo(Assembly targetAssembly)
        {
            return GetAssemblyInfo(targetAssembly, false);
        }

        /// <summary>
        /// Returns a <see cref="string"/>
        /// about the executing assembly.
        /// </summary>
        /// <param name="targetAssembly">
        /// The executing <see cref="System.Reflection.Assembly"/>.
        /// </param>
        /// <param name="useConsoleChars">
        /// When <c>true</c> selected “special” characters are formatted for the Windows Console.
        /// </param>
        /// <returns>Returns <see cref="string"/></returns>
        public static string GetAssemblyInfo(Assembly targetAssembly, bool useConsoleChars)
        {
            var sb = new StringBuilder();
            ProgramAssemblyInfo info = new ProgramAssemblyInfo(targetAssembly);

            sb.AppendFormat("{0} {1}{2}", info.AssemblyTitle, info.AssemblyVersion, Environment.NewLine);
            sb.Append(info.AssemblyDescription);
            sb.Append(Environment.NewLine);
            sb.Append(info.AssemblyCopyright);
            sb.Append(Environment.NewLine);

            return useConsoleChars ? ProgramUtility.GetConsoleCharacters(sb.ToString()) : sb.ToString();
        }

        /// <summary>
        /// Gets the directory name from assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">assembly</exception>
        public static string GetPathFromAssembly(Assembly assembly)
        {
            if (assembly == null) throw new ArgumentNullException(nameof(assembly));

#pragma warning disable SYSLIB0012
            var hasCodeBaseOnWindows =
                !string.IsNullOrWhiteSpace(assembly.CodeBase)
                &&
                !ProgramFileUtility.IsForwardSlashSystem()
                ;

            var location = hasCodeBaseOnWindows ?
                assembly.CodeBase.Replace("file:///", string.Empty) :
                assembly.Location;
#pragma warning restore SYSLIB0012

            var root = Path.GetDirectoryName(location);
            return root;
        }

        /// <summary>
        /// Gets the path from assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <param name="fileSegment">The file segment.</param>
        public static string GetPathFromAssembly(Assembly assembly, string fileSegment)
        {
            if (string.IsNullOrWhiteSpace(fileSegment)) throw new ArgumentNullException(nameof(fileSegment));

            fileSegment = ProgramFileUtility.TrimLeadingDirectorySeparatorChars(fileSegment);
            if (Path.IsPathRooted(fileSegment)) throw new FormatException("The expected relative path is not here.");

            fileSegment = ProgramFileUtility.NormalizePath(fileSegment);

            var root = GetPathFromAssembly(assembly);
            var levels = ProgramFileUtility.CountParentDirectoryChars(fileSegment);
            if (levels > 0) root = ProgramFileUtility.GetParentDirectory(root, levels);

            var path = ProgramFileUtility.GetCombinedPath(root, fileSegment);
            return path;
        }
    }
}
