// People should be able to see and report errors to the developer very easily.
//
// Unity's Developer Console only works in development builds and it only shows
// errors. This class provides a console that works in all builds and also shows
// log and warnings in development builds.
//
// Note: we don't include the stack trace, because that can also be grabbed from
// the log files if needed.
//
// Note: there is no 'hide' button because we DO want people to see those errors
// and report them back to us.
//
// Note: normal Debug.Log messages can be shown by building in Debug/Development
//       mode.
using UnityEngine;
using System.Collections.Generic;

namespace Mirror
{
    struct LogEntry
    {
        public string message;
        public LogType type;

        public LogEntry(string message, LogType type)
        {
            this.message = message;
            this.type = type;
        }
    }

    public class GUIConsole : MonoBehaviour
    {
        public int height = 80;
        public int offsetY = 40;

        // only keep the recent 'n' entries. otherwise memory would grow forever
        // and drawing would get slower and slower.
        public int maxLogCount = 50;

        // Unity Editor has the Console window, we don't need to show it there.
        // unless for testing, so keep it as option.
        public bool showInEditor = false;

        // log as queue so we can remove the first entry easily
        readonly Queue<LogEntry> log = new Queue<LogEntry>();

        // hotkey to show/hide at runtime for easier debugging
        // (sometimes we need to temporarily hide/show it)
        // Default is BackQuote, because F keys are already assigned in browsers
        [Tooltip("Hotkey to show/hide the console at runtime\nBack Quote is usually on the left above Tab\nChange with caution - F keys are generally already taken in Browsers")]
        public KeyCode hotKey = KeyCode.BackQuote;

        // GUI
        bool visible;
        Vector2 scroll = Vector2.zero;

        // only show at runtime, or if showInEditor is enabled
        bool show => !Application.isEditor || showInEditor;

        void Awake()
        {
            // only show at runtime, or if showInEditor is enabled
            if (show)
                Application.logMessageReceived += OnLog;
        }

        // OnLog logs everything, even Debug.Log messages in release builds
        // => this makes a lot of things easier. e.g. addon initialization logs.
        // => it's really better to have than not to have those
        void OnLog(string message, string stackTrace, LogType type)
        {
            // is this important?
            // => always show exceptions & errors
            // => usually a good idea to show warnings too, otherwise it's too
            //    easy to miss OnDeserialize warnings etc. in builds
            bool isImportant = type == LogType.Error || type == LogType.Exception || type == LogType.Warning;

            // use stack trace only if important
            // (otherwise users would have to find and search the log file.
            //  seeing it in the console directly is way easier to deal with.)
            // => only add \n if stack trace is available (only in debug builds)
            if (isImportant && !string.IsNullOrWhiteSpace(stackTrace))
                message += $"\n{stackTrace}";

            // add to queue
            log.Enqueue(new LogEntry(message, type));

            // respect max entries
            if (log.Count > maxLogCount)
                log.Dequeue();

            // become visible if it was important
            // (no need to become visible for regular log. let the user decide.)
            if (isImportant)
                visible = true;

            // auto scroll
            scroll.y = float.MaxValue;
        }

        void Update()
        {
            if (show && Input.GetKeyDown(hotKey))
                visible = !visible;
        }

        void OnGUI()
        {
            if (!visible) return;

            // If this offset is changed, also change width in NetworkManagerHUD::OnGUI
            int offsetX = 300 + 20;

            GUILayout.BeginArea(new Rect(offsetX, offsetY, Screen.width - offsetX - 10, height));

            scroll = GUILayout.BeginScrollView(scroll, "Box", GUILayout.Width(Screen.width - offsetX - 10), GUILayout.Height(height));
            foreach (LogEntry entry in log)
            {
                if (entry.type == LogType.Error || entry.type == LogType.Exception)
                    GUI.color = Color.red;
                else if (entry.type == LogType.Warning)
                    GUI.color = Color.yellow;

                GUILayout.Label(entry.message);
                GUI.color = Color.white;
            }
            GUILayout.EndScrollView();

            GUILayout.EndArea();
        }
    }
}
