using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Newtonsoft.Json;

namespace Jovian.Logger {
    [CustomEditor(typeof(LoggerSettings))]
    internal class LoggerSettingsEditor : Editor {
        private static SerializedObject LoggerSettingsObj;

        private SerializedProperty enableGlobalLoggingProp;
        private SerializedProperty loggerColorsProp;


        // For loggerColors sub-fields
        private SerializedProperty infoColorProp;
        private SerializedProperty warningColorProp;
        private SerializedProperty errorColorProp;
        private SerializedProperty assertColorProp;
        private SerializedProperty exceptionColorProp;
        private SerializedProperty spamColorProp;

        private static Editor editor;
        private bool showLocalFilters = true;
        private bool showAllFilters = true;
        private bool showGlobalFilters = true;
        private string globalCallerNames = "";
        private string localCallerNames = "";
        private bool hasGlobalSavedFilter = true;
        private bool hasLocalSavedFilter = true;

        private class ListOfFilters {
            public List<Filters> filters;
        }

        private void LoadLocalFilters() {
            var loggerSettings = (LoggerSettings)target;
            var filters = EditorPrefs.GetString("LoggerFilters");
            if(UnityIsHeadless()) {
                filters = null;
            }
            if(!string.IsNullOrEmpty(filters)) {
                loggerSettings.LocalFilters = JsonConvert.DeserializeObject<ListOfFilters>(filters).filters;
            }

            if(loggerSettings.LocalFilters == null) {
                loggerSettings.LocalFilters = new List<Filters>(0);
            }
        }

        private bool UnityIsHeadless() {
            if(Environment.CommandLine.Contains("-batchmode")) {
                return true;
            }

            return false;
        }

        private void OnEnable() {
            enableGlobalLoggingProp = serializedObject.FindProperty("enableGlobalLogging");
            loggerColorsProp = serializedObject.FindProperty("loggerColors");

            infoColorProp = loggerColorsProp.FindPropertyRelative("infoColor");
            warningColorProp = loggerColorsProp.FindPropertyRelative("warningColor");
            errorColorProp = loggerColorsProp.FindPropertyRelative("errorColor");
            assertColorProp = loggerColorsProp.FindPropertyRelative("assertColor");
            exceptionColorProp = loggerColorsProp.FindPropertyRelative("exceptionColor");
            spamColorProp = loggerColorsProp.FindPropertyRelative("spamColor");

            LoadLocalFilters();
        }

        public static void ShowSettings() {
            var loggerSettings = new AssetSettingsLoader<LoggerSettings>().GetSettings(LoggerSettingsProvider.SETTINGS_FILE);
            LoggerSettingsObj = new SerializedObject(loggerSettings);
            LoggerSettingsObj.Update();
            editor ??= CreateEditor(loggerSettings);
            editor.OnInspectorGUI();
        }

        public override void OnInspectorGUI() {
            serializedObject.Update();
            var loggerSettings = (LoggerSettings)target;

            EditorGUILayout.Space(15);
            EditorGUILayout.LabelField("Custom Logger Settings", EditorStyles.whiteLargeLabel);
            EditorGUILayout.Space(15);
            EditorGUILayout.HelpBox("This will enable/disable logging for the entire project. It has a code level method as well.", MessageType.Warning);
            EditorGUILayout.Space(3);
            EditorGUILayout.PropertyField(enableGlobalLoggingProp, new GUIContent("Enable Global Logging"));

            EditorGUILayout.Space(15);
            EditorGUILayout.LabelField("Log Message Colors", EditorStyles.whiteLargeLabel);
            EditorGUILayout.Space(3);
            EditorGUILayout.PropertyField(infoColorProp, new GUIContent("Info Color"));
            EditorGUILayout.PropertyField(warningColorProp, new GUIContent("Warning Color"));
            EditorGUILayout.PropertyField(errorColorProp, new GUIContent("Error Color"));
            EditorGUILayout.PropertyField(assertColorProp, new GUIContent("Assert Color"));
            EditorGUILayout.PropertyField(exceptionColorProp, new GUIContent("Exception Color"));
            EditorGUILayout.PropertyField(spamColorProp, new GUIContent("Spam Color"));

            EditorGUILayout.Space(10);
            if(GUILayout.Button("Reset To Default Colors")) {
                loggerSettings.ResetColorsToDefault();
                EditorUtility.SetDirty(loggerSettings);
            }

            EditorGUILayout.Space(20);
            EditorGUILayout.LabelField("Log Uploader Settings", EditorStyles.whiteLargeLabel);
            EditorGUILayout.Space(3);

            EditorGUILayout.Space(30);
            showAllFilters = EditorGUILayout.Foldout(showAllFilters, "Logger Filters");
            if(showAllFilters) {
                EditorGUILayout.Space(20);
                EditorGUILayout.LabelField("Global Project Level Filters", EditorStyles.whiteLargeLabel);
                EditorGUILayout.Space(3);
                EditorGUILayout.HelpBox("Global filters will be saved with the asset and, if commited, applied to everyone using the project", MessageType.Warning);
                EditorGUILayout.Space(3);
                showGlobalFilters = EditorGUILayout.Foldout(showGlobalFilters, "Active Filters");
                if(showGlobalFilters) {
                    for(int i = 0; i < loggerSettings.globalFilters.Length; i++) {
                        EditorGUILayout.BeginVertical("box");
                        loggerSettings.globalFilters[i].logCategory = (LogCategory)EditorGUILayout.EnumFlagsField("Log Category", loggerSettings.globalFilters[i].logCategory);
                        loggerSettings.globalFilters[i].jovianLogType = (JovianLogType)EditorGUILayout.EnumPopup("Log Type", loggerSettings.globalFilters[i].jovianLogType);
                        EditorGUILayout.BeginHorizontal();
                        globalCallerNames = GetCallersAsString(loggerSettings.globalFilters[i].callerNames);
                        globalCallerNames = EditorGUILayout.TextField("Caller Name", globalCallerNames);
                        loggerSettings.globalFilters[i].callerListingType = (CallerListingType)EditorGUILayout.EnumPopup("", loggerSettings.globalFilters[i].callerListingType, GUILayout.Width(120));
                        EditorGUILayout.EndHorizontal();
                        loggerSettings.globalFilters[i].callerNames = globalCallerNames.Split(",").ToList();

                        EditorGUILayout.BeginHorizontal();
                        GUILayout.FlexibleSpace();
                        if(GUILayout.Button("Remove This Filter")) {
                            var temp = loggerSettings.globalFilters.ToList();
                            temp.RemoveAt(i);
                            loggerSettings.globalFilters = temp.ToArray();
                            EditorUtility.SetDirty(loggerSettings);
                            AssetDatabase.SaveAssets();
                            EditorUtility.RequestScriptReload();
                        }

                        if(GUILayout.Button("Save Changes")) {
                            hasGlobalSavedFilter = true;
                            EditorUtility.SetDirty(loggerSettings);
                            AssetDatabase.SaveAssets();
                            EditorUtility.RequestScriptReload();
                        }

                        EditorGUILayout.EndHorizontal();

                        if (!hasGlobalSavedFilter) {
                            GUILayout.Space(10);
                            EditorGUILayout.BeginHorizontal();
                            GUILayout.FlexibleSpace();
                            EditorGUILayout.HelpBox("Filter Not Saved...", MessageType.Error);
                            EditorGUILayout.EndHorizontal();
                            GUILayout.Space(5);
                        }

                        EditorGUILayout.EndVertical();
                        EditorGUILayout.Space();
                    }

                    EditorGUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    if(GUILayout.Button("Add Global Filter")) {
                        var temp = loggerSettings.globalFilters.ToList();
                        temp.Add(new Filters());
                        loggerSettings.globalFilters = temp.ToArray();
                        hasGlobalSavedFilter = false;
                    }

                    GUILayout.FlexibleSpace();
                    EditorGUILayout.EndHorizontal();
                }

                EditorGUILayout.Space(20);
                EditorGUILayout.LabelField("Local Editor Level Filters (Not Serialized)", EditorStyles.whiteLargeLabel);
                EditorGUILayout.Space(3);
                EditorGUILayout.HelpBox("Local filters are local and will not be saved with the asset but rather with the editor settings. Use these to set personal filters.", MessageType.Info);
                EditorGUILayout.Space(3);
                showLocalFilters = EditorGUILayout.Foldout(showLocalFilters, "Active Filters");
                if(showLocalFilters) {
                    for(int i = 0; i < loggerSettings.LocalFilters.Count; i++) {
                        EditorGUILayout.BeginVertical("box");
                        EditorGUI.BeginChangeCheck();
                        loggerSettings.LocalFilters[i].logCategory = (LogCategory)EditorGUILayout.EnumFlagsField("Log Category", loggerSettings.LocalFilters[i].logCategory);
                        loggerSettings.LocalFilters[i].jovianLogType = (JovianLogType)EditorGUILayout.EnumPopup("Log Type", loggerSettings.LocalFilters[i].jovianLogType);
                        EditorGUILayout.BeginHorizontal();
                        localCallerNames = GetCallersAsString(loggerSettings.LocalFilters[i].callerNames);
                        localCallerNames = EditorGUILayout.TextField("Caller Name", localCallerNames, GUILayout.MinWidth(400));
                        loggerSettings.LocalFilters[i].callerListingType = (CallerListingType)EditorGUILayout.EnumPopup("", loggerSettings.LocalFilters[i].callerListingType, GUILayout.Width(120));
                        EditorGUILayout.EndHorizontal();
                        loggerSettings.LocalFilters[i].callerNames = localCallerNames.Split(",").ToList();

                        EditorGUILayout.BeginHorizontal();
                        GUILayout.FlexibleSpace();
                        if(GUILayout.Button("Remove This Filter")) {
                            var temp = new List<Filters>(loggerSettings.LocalFilters);
                            temp.RemoveAt(i);
                            loggerSettings.LocalFilters = temp;
                            var filters = JsonConvert.SerializeObject(new ListOfFilters() { filters = loggerSettings.LocalFilters });
                            EditorPrefs.SetString("LoggerFilters", filters);
                            EditorUtility.RequestScriptReload();
                        }

                        if(GUILayout.Button("Save Changes")) {
                            var filters = JsonConvert.SerializeObject(new ListOfFilters() { filters = loggerSettings.LocalFilters });
                            EditorPrefs.SetString("LoggerFilters", filters);
                            EditorUtility.RequestScriptReload();
                        }

                        EditorGUILayout.EndHorizontal();

                        if (!hasLocalSavedFilter) {
                            GUILayout.Space(10);
                            EditorGUILayout.BeginHorizontal();
                            GUILayout.FlexibleSpace();
                            EditorGUILayout.HelpBox("Filter Not Saved...", MessageType.Error);
                            EditorGUILayout.EndHorizontal();
                            GUILayout.Space(5);
                        }

                        EditorGUILayout.EndVertical();
                        EditorGUILayout.Space();
                    }

                    EditorGUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    if(GUILayout.Button("Add Local Filter")) {
                        loggerSettings.LocalFilters.Add(new Filters());
                        hasLocalSavedFilter = false;
                    }

                    GUILayout.FlexibleSpace();
                    EditorGUILayout.EndHorizontal();
                }
            }

            serializedObject.ApplyModifiedProperties();
        }

        private string GetCallersAsString(List<string> callerNames) {
            if(callerNames == null || callerNames.Count == 0) {
                return "";
            }

            string result = "";
            for(int i = 0; i < callerNames.Count; i++) {
                result += callerNames[i];
                if(i != callerNames.Count - 1) {
                    result += ",";
                }
            }

            return result;
        }

        private void OnDisable() {
            AssetDatabase.SaveAssets();
        }

        private void OnDestroy() {
            AssetDatabase.SaveAssets();
        }
    }
}
