﻿using System;
using System.IO;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
using System.Collections.Generic;
using Castle.Core.Logging;

namespace RalphsDotNet.Service
{
    public static class Log
    {
        private static string _logDir = Directory.GetCurrentDirectory ();
        private static ushort _logLevel = 1;
        private static Dictionary<LogType, string> _paths = new Dictionary<LogType, string> ();

        public static void SetDirectory (string dir)
        {
            if (!Directory.Exists (dir))
                Directory.CreateDirectory (dir);

            _paths.Clear ();
            _logDir = dir;
        }

        public static void SetLogLevel (ushort logLevel)
        {
            if (logLevel < 0 || logLevel > 5)
                throw(new ArgumentException ("loglevel has to be between 0 and 5 where 0 is off, 1 debug and five fatal"));
        }

        public static void Debug (Exception ex)
        {
            Write (ex, LogType.Debug);
        }

        public static void Info (Exception ex)
        {
            Write (ex, LogType.Info);
        }

        public static void Warning (Exception ex)
        {
            Write (ex, LogType.Warning);
        }

        public static void Error (Exception ex)
        {
            Write (ex, LogType.Error);
        }

        public static void Fatal (Exception ex)
        {
            Write (ex, LogType.Fatal);
        }

        public static void Debug (string msg)
        {
            Write (msg, LogType.Debug);
        }

        public static void Info (string msg)
        {
            Write (msg, LogType.Info);
        }

        public static void Warning (string msg)
        {
            Write (msg, LogType.Warning);
        }

        public static void Error (string msg)
        {
            Write (msg, LogType.Error);
        }

        public static void Fatal (string msg)
        {
            Write (msg, LogType.Fatal);
        }

        public static void Write (Exception ex, LogType type)
        {
            #if DEBUG
            Write (ex.ToString (), type);
            #else
            Write(ex.Message, type);
            #endif
        }

        public static void Write (string msg, LogType type)
        {
            if (type == LogType.All)
                throw(new ArgumentException ("given LogType can not be All")); 

            if (_logLevel > 0 && (ushort)type >= _logLevel) {
                try {
                    var line = $"{DateTime.Now.ToShortDateString ()} {DateTime.Now.ToShortTimeString ()}: {msg} {Environment.NewLine}";
                    var lineAll = $"{type.ToString ().ToUpper ()} - {DateTime.Now.ToShortDateString ()} {DateTime.Now.ToShortTimeString ()}: {msg} {Environment.NewLine}";

                    #if DEBUG
                    if (type == LogType.Error || type == LogType.Fatal)
                        Console.Error.Write (lineAll);
                    else
                        Console.WriteLine (lineAll);
                    #endif

                    File.AppendAllText (GetPath (type), line);
                    File.AppendAllText (GetPath (LogType.All), lineAll);
                } catch (Exception ex) {
                    throw(new LoggingException (ex));
                }
            }
        }

        private static string GetPath (LogType type)
        {
            if (_paths.Count == 0)
                GeneratePaths ();

            return _paths [type];
        }

        private static void GeneratePaths ()
        {
            var generatePath = new Action<LogType> (t => _paths.Add (t, Path.Combine (_logDir, $"{t.ToString ().ToLower ()}.log")));

            generatePath (LogType.All);
            generatePath (LogType.Debug);
            generatePath (LogType.Info);
            generatePath (LogType.Warning);
            generatePath (LogType.Error);
            generatePath (LogType.Fatal);
        }
    }

    public class LoggingException:Exception
    {
        public LoggingException (Exception ex) : base ("writing log entry failed", ex)
        {
        }
    }

    public enum LogType : ushort
    {
        All = 0,
        Debug = 1,
        Info = 2,
        Warning = 3,
        Error = 4,
        Fatal = 5
    }
}

