using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Jovian.Logger {
    /// <summary>
    /// Default logger for Custom Logger in case you don't want to create a new instance of CustomLogger
    /// Recommended is to create a new instance of CustomLogger
    /// </summary>
    public static class GlobalLogger {

        /// <summary>
        /// Logs messages that are meant to only be seen in the editor
        /// </summary>
        /// <param name="msg">Log message</param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [Conditional("UNITY_EDITOR")] [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogSpam(string msg, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogSpam($"{GetCaller()?.Name}.{callerMethod}", msg, logCat);
        }

        /// <summary>
        /// Logs messages that are meant to only be seen in the editor with a context object
        /// </summary>
        /// <param name="msg">Log message</param>
        /// <param name="context">Object to select in scene/project</param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [Conditional("UNITY_EDITOR")] [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogSpam(string msg, UnityEngine.Object context, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogSpam($"{GetCaller()?.Name}.{callerMethod}", msg, logCat, context);
        }

        /// <summary>
        /// Logs standard Info/Debug messages
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogInfo(string msg, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogInfo($"{GetCaller()?.Name}.{callerMethod}", msg, logCat);
        }

        /// <summary>
        /// Logs standard Info/Debug messagesc with a context object
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="context">Object to select in scene/project</param>
        /// <param name="logCat"></param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogInfo(string msg, UnityEngine.Object context, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogInfo($"{GetCaller()?.Name}.{callerMethod}", msg, logCat, context);
        }

        /// <summary>
        /// Logs standard Warning messages
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogWarning(string msg, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogWarning($"{GetCaller()?.Name}.{callerMethod}", msg, logCat);
        }

        /// <summary>
        /// Logs standard Warning messages with a context object
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="context">Object to select in scene/project</param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogWarning(string msg, UnityEngine.Object context, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogWarning($"{GetCaller()?.Name}.{callerMethod}", msg, logCat, context);
        }

        /// <summary>
        /// Logs standard Error messages
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogError(string msg, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogError($"{GetCaller()?.Name}.{callerMethod}", msg, logCat);
        }

        /// <summary>
        /// Logs standard Error messages with a context object
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="context">Object to select in scene/project</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogError(string msg, UnityEngine.Object context, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogError($"{GetCaller()?.Name}.{callerMethod}", msg, logCat, context);
        }

        /// <summary>
        /// Logs standard Assert messages
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogAssert(string msg, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogAssert($"{GetCaller()?.Name}.{callerMethod}", msg, logCat);
        }

        /// <summary>
        /// Logs standard Assert messages with a context object
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="context">Object to select in scene/project</param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogAssert(string msg, UnityEngine.Object context, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogAssert($"{GetCaller()?.Name}.{callerMethod}", msg, logCat, context);
        }


        /// <summary>
        /// Logs string Exception messages
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogException(string msg, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogException($"{GetCaller()?.Name}.{callerMethod}", msg, logCat);
        }

        /// <summary>
        /// Logs string Exception messages with a context object
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="context">Object to select in scene/project</param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogException(string msg, UnityEngine.Object context, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogException($"{GetCaller()?.Name}.{callerMethod}", msg, logCat, context);
        }

        /// <summary>
        /// Logs standard Exception messages
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogException(Exception e, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogException($"{GetCaller()?.Name}.{callerMethod}", e.Message, logCat);
        }

        /// <summary>
        /// Logs standard Exception messages with a context object
        /// </summary>
        /// <param name="e"></param>
        /// <param name="context">Object to select in scene/project</param>
        /// <param name="logCat">Optional Log Category</param>
        /// <param name="callerMethod">Implicit, do not provide</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void LogException(Exception e, UnityEngine.Object context, LogCategory logCat, [CallerMemberName] string callerMethod = "") {
            InternalLogger.LogException($"{GetCaller()?.Name}.{callerMethod}", e.Message, logCat, context);
        }

        private static Type GetCaller() {
            var stackTrace = new StackTrace();
            var stackFrame = stackTrace.GetFrame(2);
            var caller = stackFrame?.GetMethod()?.DeclaringType;
            return caller;
        }
    }
}
