﻿using System;
using log4net.Appender;
using log4net.Repository.Hierarchy;
using log4net.Layout;
using log4net.Core;
using log4net;
using System.Collections.Generic;

namespace SelfMediaDatabase.Logging
{
    /// <summary>
    /// Provides tools for logging
    /// </summary>
    public static class LogHelper
    {

        internal static ILogOptions LogOptions = null;

        /// <summary>
        /// Gets the logger.
        /// </summary>
        /// <returns>The logger.</returns>
        /// <param name="declaringType">The declaring type.</param>
        public static ILog GetLogger(Type declaringType)
        {
            return new Log(log4net.LogManager.GetLogger(declaringType));
        }

        /// <summary>
        /// Initializes and enable the logging and his <paramref name="level"/>.
        /// </summary>
        /// <param name="level">The level of logging (verbosity).</param>
        public static void InitializeLogging(ILogOptions logOptions)
        {
            LogOptions = logOptions;
            if (logOptions.LogLevel == LogLevel.None) logOptions.LogLevel = LogLevel.Info;
            if (logOptions.LogPattern == null) logOptions.LogPattern = "[%-5level] %message%newline";

            Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
            hierarchy.Root.RemoveAllAppenders();
            hierarchy.Root.Level = logOptions.LogLevel.ToLog4NetLevel();

            var appenders = new List<IAppender>();

            //appenders.Add(CreateConsoleAppender(logOptions));
            appenders.Add(CreateColoredConsoleAppender(logOptions));
            if (!string.IsNullOrWhiteSpace(logOptions.LogFilePath))
                appenders.Add(CreateFileAppender(logOptions));

            log4net.Config.BasicConfigurator.Configure(appenders.ToArray());       
        }

        /// <summary>
        /// Convert <see cref="LogLevel"/> to log4net <see cref="Level">level</see>.
        /// </summary>
        /// <param name="level">The level to convert.</param>
        /// <returns>The log4net level.</returns>
        public static Level ToLog4NetLevel(this LogLevel level)
        {
            switch (level)
            {
                case LogLevel.T:
                case LogLevel.Trace:
                    return Level.Trace;
                case LogLevel.D:
                case LogLevel.Debug:
                    return Level.Debug;
                case LogLevel.I:
                case LogLevel.Info:
                    return Level.Info;
                case LogLevel.W:
                case LogLevel.Warn:
                    return Level.Warn;
                case LogLevel.E:
                case LogLevel.Error:
                    return Level.Error;
                case LogLevel.F:
                case LogLevel.Fatal:
                    return Level.Fatal;
                default:
                    return Level.Info;
            }
        }

        /// <summary>
        /// Creates the console appender according options.
        /// </summary>
        /// <returns>The created console appender.</returns>
        /// <param name="logOptions">The log options.</param>
        private static ConsoleAppender CreateConsoleAppender(ILogOptions logOptions)
        {
            var appender = new ConsoleAppender();
            var pl = new PatternLayout();
            pl.ConversionPattern = logOptions.LogPattern;
            pl.ActivateOptions();
            appender.Layout = pl;
            appender.ActivateOptions();

            return appender;
        }

        /// <summary>
        /// Creates the console appender according options.
        /// </summary>
        /// <returns>The created console appender.</returns>
        /// <param name="logOptions">The log options.</param>
        private static IAppender CreateColoredConsoleAppender(ILogOptions logOptions)
        {
            var appender = new AnsiColorTerminalAppender();
            var pl = new PatternLayout();
            pl.ConversionPattern = logOptions.LogPattern;
            pl.ActivateOptions();
            appender.Layout = pl;
            appender.ActivateOptions();

            appender.AddMapping(new AnsiColorTerminalAppender.LevelColors() { Level = Level.Trace, ForeColor = AnsiColorTerminalAppender.AnsiColor.Blue });
            appender.AddMapping(new AnsiColorTerminalAppender.LevelColors() { Level = Level.Debug, ForeColor = AnsiColorTerminalAppender.AnsiColor.White });
            appender.AddMapping(new AnsiColorTerminalAppender.LevelColors() { Level = Level.Info, ForeColor = AnsiColorTerminalAppender.AnsiColor.Green });
            appender.AddMapping(new AnsiColorTerminalAppender.LevelColors() { Level = Level.Warn, ForeColor = AnsiColorTerminalAppender.AnsiColor.Yellow });
            appender.AddMapping(new AnsiColorTerminalAppender.LevelColors() { Level = Level.Error, ForeColor = AnsiColorTerminalAppender.AnsiColor.Red });
            appender.AddMapping(new AnsiColorTerminalAppender.LevelColors() { Level = Level.Fatal, BackColor = AnsiColorTerminalAppender.AnsiColor.Red, ForeColor = AnsiColorTerminalAppender.AnsiColor.White });

            return appender;
        }

        private static FileAppender CreateFileAppender(ILogOptions logOptions)
        {
            var appender = new FileAppender();
            var pl = new PatternLayout();
            pl.ConversionPattern = logOptions.LogPattern;
            pl.ActivateOptions();
            appender.Layout = pl;
            appender.ActivateOptions();
            appender.AppendToFile = true;
            appender.File = logOptions.LogFilePath;

            return appender;
        }
    }
}

