/* ---------------------------------------
 * Author:          Martin Pane (martintayx@gmail.com) (@tayx94)
 * Contributors:    https://github.com/Tayx94/graphy/graphs/contributors
 * Project:         Graphy - Ultimate Stats Monitor
 * Date:            02-Jan-18
 * Studio:          Tayx
 *
 * Git repo:        https://github.com/Tayx94/graphy
 *
 * This project is released under the MIT license.
 * Attribution is not required, but it is always welcomed!
 * -------------------------------------*/

using System;
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using UnityEditor;

namespace Tayx.Graphy
{
    [CustomEditor( typeof( GraphyDebugger ) )]
    internal class GraphyDebuggerEditor : Editor
    {
        #region Variables -> Private

        private GraphyDebugger m_target;

        private int m_newDebugPacketListSize = 0;

        private int m_previouslySelectedDebugPacketIndex = 0;
        private int m_currentlySelectedDebugPacketIndex = 0;

        private int m_selectedDebugPacketCondition = 0;

        #endregion

        #region Methods -> Unity Callbacks

        private void OnEnable()
        {
            m_target = (GraphyDebugger) target;
        }

        #endregion

        #region Methods -> Public Override

        public override void OnInspectorGUI()
        {
            if( m_target == null && target == null )
            {
                base.OnInspectorGUI();

                return;
            }

            float defaultLabelWidth = EditorGUIUtility.labelWidth;
            float defaultFieldWidth = EditorGUIUtility.fieldWidth;

            //===== CONTENT REGION ========================================================================

            GUILayout.Space( 20 );

            #region Section -> Logo

            if( GraphyEditorStyle.DebuggerLogoTexture != null )
            {
                GUILayout.Label
                (
                    image: GraphyEditorStyle.DebuggerLogoTexture,
                    style: new GUIStyle( GUI.skin.GetStyle( "Label" ) )
                    {
                        alignment = TextAnchor.UpperCenter
                    }
                );

                GUILayout.Space( 10 );
            }
            else
            {
                EditorGUILayout.LabelField
                (
                    label: "[ GRAPHY - DEBUGGER ]",
                    style: GraphyEditorStyle.HeaderStyle1
                );
            }

            #endregion

            GUILayout.Space( 5 ); //Extra pixels added when the logo is used.

            #region Section -> Settings

            SerializedObject serObj = serializedObject;

            SerializedProperty
                debugPacketList =
                    serObj.FindProperty( "m_debugPackets" ); // Find the List in our script and create a refrence of it

            //Update our list
            serObj.Update();

            EditorGUILayout.LabelField( "Current [Debug Packets] list size: " + debugPacketList.arraySize );

            EditorGUIUtility.fieldWidth = 32;
            EditorGUILayout.BeginHorizontal();


            m_newDebugPacketListSize = EditorGUILayout.IntField
            (
                label: "Define a new list size",
                value: m_newDebugPacketListSize
            );

            if( GUILayout.Button( "Resize List" ) )
            {
                if( EditorUtility.DisplayDialog
                    (
                        title:
                        "Resize List",
                        message:
                        "Are you sure you want to resize the entire List?\n\n" +
                        "Current List Size -> " +
                        debugPacketList.arraySize +
                        "\n" +
                        "New List Size -> " +
                        m_newDebugPacketListSize +
                        "\n" +
                        "This will add default entries if the value is greater than the list size, or erase the bottom values until the new size specified.",
                        ok:
                        "Resize",
                        cancel:
                        "Cancel" )
                )
                {
                    m_currentlySelectedDebugPacketIndex = 0;

                    if( m_newDebugPacketListSize != debugPacketList.arraySize )
                    {
                        while( m_newDebugPacketListSize > debugPacketList.arraySize )
                        {
                            debugPacketList.InsertArrayElementAtIndex( debugPacketList.arraySize );
                            SetDefaultDebugPacketValues( debugPacketList );
                        }

                        while( m_newDebugPacketListSize < debugPacketList.arraySize )
                        {
                            debugPacketList.DeleteArrayElementAtIndex( debugPacketList.arraySize - 1 );
                        }
                    }
                }
            }

            EditorGUILayout.EndHorizontal();

            EditorGUILayout.LabelField( "NOT RECOMMENDED (Only use for first initialization)",
                EditorStyles.centeredGreyMiniLabel );

            EditorGUILayout.Space();
            EditorGUILayout.Space();

            if( debugPacketList.arraySize < 1 )
            {
                m_previouslySelectedDebugPacketIndex = 0;
                m_currentlySelectedDebugPacketIndex = 0;
                m_selectedDebugPacketCondition = 0;

                serializedObject.ApplyModifiedProperties();
                return;
            }

            GraphyEditorStyle.HeaderStyle2.contentOffset = Vector2.down * 3f;

            EditorGUILayout.LabelField( "Selected debug packet:" );

            EditorGUILayout.BeginHorizontal();

            List<string> debugPacketNames = new List<string>();
            for( int i = 0; i < debugPacketList.arraySize; i++ )
            {
                SerializedProperty listItem = debugPacketList.GetArrayElementAtIndex( i );
                // NOTE: If the Popup detects two equal strings, it just paints 1, that's why I always add the "i"
                char checkMark = listItem.FindPropertyRelative( "Active" ).boolValue ? '\u2714' : '\u2718';
                debugPacketNames.Add
                (
                    (i + 1) +
                    " (" +
                    checkMark +
                    ") " +
                    " - ID: " +
                    listItem.FindPropertyRelative( "Id" ).intValue +
                    " (Conditions: " +
                    listItem.FindPropertyRelative( "DebugConditions" ).arraySize +
                    ")"
                );
            }

            m_currentlySelectedDebugPacketIndex =
                EditorGUILayout.Popup( m_currentlySelectedDebugPacketIndex, debugPacketNames.ToArray() );

            if( m_currentlySelectedDebugPacketIndex != m_previouslySelectedDebugPacketIndex )
            {
                m_selectedDebugPacketCondition = 0;

                m_previouslySelectedDebugPacketIndex = m_currentlySelectedDebugPacketIndex;
            }

            Color defaultGUIColor = GUI.color;

            GUI.color = new Color( 0.7f, 1f, 0.0f, 1f );

            //Or add a new item to the List<> with a button

            if( GUILayout.Button( "Add", GUILayout.Width( 60 ) ) )
            {
                debugPacketList.InsertArrayElementAtIndex( debugPacketList.arraySize );
                SetDefaultDebugPacketValues( debugPacketList );
            }

            GUI.color = new Color( 1f, 0.7f, 0.0f, 1f );

            //Remove this index from the List

            if( GUILayout.Button( "Remove", GUILayout.Width( 60 ) ) )
            {
                debugPacketList.DeleteArrayElementAtIndex( m_currentlySelectedDebugPacketIndex );
                if( m_currentlySelectedDebugPacketIndex > 0 )
                {
                    m_currentlySelectedDebugPacketIndex--;
                }

                if( debugPacketList.arraySize < 1 )
                {
                    serializedObject.ApplyModifiedProperties();
                    return;
                }
            }

            GUI.color = defaultGUIColor;

            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Space();
            EditorGUILayout.Space();
            EditorGUILayout.Space();

            //Display our list to the inspector window

            SerializedProperty listItemSelected =
                debugPacketList.GetArrayElementAtIndex( m_currentlySelectedDebugPacketIndex );

            SerializedProperty Active = listItemSelected.FindPropertyRelative( "Active" );
            SerializedProperty Id = listItemSelected.FindPropertyRelative( "Id" );
            SerializedProperty ExecuteOnce = listItemSelected.FindPropertyRelative( "ExecuteOnce" );
            SerializedProperty InitSleepTime = listItemSelected.FindPropertyRelative( "InitSleepTime" );
            SerializedProperty ExecuteSleepTime = listItemSelected.FindPropertyRelative( "ExecuteSleepTime" );
            SerializedProperty ConditionEvaluation = listItemSelected.FindPropertyRelative( "ConditionEvaluation" );
            SerializedProperty DebugConditions = listItemSelected.FindPropertyRelative( "DebugConditions" );
            SerializedProperty MessageType = listItemSelected.FindPropertyRelative( "MessageType" );
            SerializedProperty Message = listItemSelected.FindPropertyRelative( "Message" );
            SerializedProperty TakeScreenshot = listItemSelected.FindPropertyRelative( "TakeScreenshot" );
            SerializedProperty ScreenshotFileName = listItemSelected.FindPropertyRelative( "ScreenshotFileName" );
            SerializedProperty DebugBreak = listItemSelected.FindPropertyRelative( "DebugBreak" );
            SerializedProperty UnityEvents = listItemSelected.FindPropertyRelative( "UnityEvents" );

            #endregion

            EditorGUILayout.LabelField
            (
                label:
                "[ PACKET ] - ID: " +
                Id.intValue +
                " (Conditions: " +
                DebugConditions.arraySize +
                ")",
                style: GraphyEditorStyle.HeaderStyle2
            );

            EditorGUIUtility.labelWidth = 150;
            EditorGUIUtility.fieldWidth = 35;

            Active.boolValue = EditorGUILayout.Toggle
            (
                new GUIContent
                (
                    text: "Active",
                    tooltip: "If false, it will not be checked"
                ),
                value: Active.boolValue
            );

            Id.intValue = EditorGUILayout.IntField
            (
                new GUIContent
                (
                    text: "ID",
                    tooltip: "Optional Id. It's used to get or remove DebugPackets in runtime"
                ),
                value: Id.intValue
            );

            ExecuteOnce.boolValue = EditorGUILayout.Toggle
            (
                new GUIContent
                (
                    text: "Execute once",
                    tooltip: "If true, once the actions are executed, this DebugPacket will delete itself"
                ),
                value: ExecuteOnce.boolValue
            );

            InitSleepTime.floatValue = EditorGUILayout.FloatField
            (
                new GUIContent
                (
                    text: "Init sleep time",
                    tooltip:
                    "Time to wait before checking if conditions are met (use this to avoid low fps drops triggering the conditions when loading the game)"
                ),
                value: InitSleepTime.floatValue
            );

            ExecuteSleepTime.floatValue = EditorGUILayout.FloatField
            (
                new GUIContent
                (
                    text: "Sleep time after execute",
                    tooltip:
                    "Time to wait before checking if conditions are met again (once they have already been met and if ExecuteOnce is false)"
                ),
                value: ExecuteSleepTime.floatValue
            );


            EditorGUIUtility.labelWidth = defaultLabelWidth;
            EditorGUIUtility.fieldWidth = defaultFieldWidth;

            EditorGUILayout.Space();
            EditorGUILayout.Space();

            EditorGUILayout.LabelField( "[ CONDITIONS ] (" + DebugConditions.arraySize + ")",
                GraphyEditorStyle.HeaderStyle2 );

            EditorGUILayout.PropertyField
            (
                ConditionEvaluation,
                new GUIContent( "Condition evaluation" )
            );

            EditorGUILayout.Space();

            if( DebugConditions.arraySize < 1 )
            {
                DebugConditions.InsertArrayElementAtIndex( DebugConditions.arraySize );
                m_selectedDebugPacketCondition = 0;
            }

            EditorGUILayout.BeginHorizontal();

            List<string> debugPacketConditionNames = new List<string>();
            for( int i = 0; i < DebugConditions.arraySize; i++ )
            {
                SerializedProperty listItem = DebugConditions.GetArrayElementAtIndex( i );
                // NOTE: If the Popup detects two equal strings, it just paints 1, that's why I always add the "i"

                string conditionName = (i + 1).ToString() + " - ";
                conditionName +=
                    GetComparerStringFromDebugVariable(
                        (GraphyDebugger.DebugVariable) listItem.FindPropertyRelative( "Variable" ).intValue ) + " ";
                conditionName +=
                    GetComparerStringFromDebugComparer(
                        (GraphyDebugger.DebugComparer) listItem.FindPropertyRelative( "Comparer" ).intValue ) + " ";
                conditionName += listItem.FindPropertyRelative( "Value" ).floatValue.ToString();

                debugPacketConditionNames.Add( conditionName );
            }

            m_selectedDebugPacketCondition =
                EditorGUILayout.Popup( m_selectedDebugPacketCondition, debugPacketConditionNames.ToArray() );

            GUI.color = new Color( 0.7f, 1f, 0.0f, 1f );

            if( GUILayout.Button( "Add", GUILayout.Width( 60 ) ) )
            {
                DebugConditions.InsertArrayElementAtIndex( DebugConditions.arraySize );
            }

            if( DebugConditions.arraySize > 1 )
            {
                GUI.color = new Color( 1f, 0.7f, 0.0f, 1f );
            }
            else
            {
                GUI.color = new Color( 1f, 0.7f, 0.0f, 0.5f );
            }

            //Remove this index from the List
            if( GUILayout.Button( "Remove", GUILayout.Width( 60 ) ) )
            {
                if( DebugConditions.arraySize > 1 )
                {
                    DebugConditions.DeleteArrayElementAtIndex( m_selectedDebugPacketCondition );
                    if( m_selectedDebugPacketCondition > 0 )
                    {
                        m_selectedDebugPacketCondition--;
                    }
                }
            }

            GUI.color = defaultGUIColor;

            EditorGUILayout.EndHorizontal();

            SerializedProperty conditionListItemSelected =
                DebugConditions.GetArrayElementAtIndex( m_selectedDebugPacketCondition );

            SerializedProperty Variable = conditionListItemSelected.FindPropertyRelative( "Variable" );
            SerializedProperty Comparer = conditionListItemSelected.FindPropertyRelative( "Comparer" );
            SerializedProperty Value = conditionListItemSelected.FindPropertyRelative( "Value" );

            EditorGUILayout.PropertyField
            (
                Variable,
                new GUIContent( "Variable" )
            );

            EditorGUILayout.PropertyField
            (
                Comparer,
                new GUIContent( "Comparer" )
            );

            EditorGUILayout.PropertyField
            (
                Value,
                new GUIContent( "Value" )
            );

            EditorGUILayout.Space();
            EditorGUILayout.Space();

            EditorGUILayout.LabelField( "[ ACTIONS ]", GraphyEditorStyle.HeaderStyle2 );

            EditorGUIUtility.labelWidth = 140;
            EditorGUIUtility.fieldWidth = 35;

            EditorGUILayout.PropertyField
            (
                MessageType,
                new GUIContent( "Message type" )
            );

            EditorGUILayout.PropertyField( Message );

            TakeScreenshot.boolValue = EditorGUILayout.Toggle
            (
                new GUIContent
                (
                    text: "Take screenshot",
                    tooltip:
                    "If true, it takes a screenshot and stores it. The location where the image is written to can include a directory/folder list. With no directory/folder list the image will be written into the Project folder. On mobile platforms the filename is appended to the persistent data path."
                ),
                value: TakeScreenshot.boolValue
            );

            if( TakeScreenshot.boolValue )
            {
                EditorGUILayout.PropertyField
                (
                    ScreenshotFileName,
                    new GUIContent
                    (
                        text: "Screenshot file name",
                        tooltip:
                        "Avoid this characters: * . \" / \\ [ ] : ; | = , \n\nIt will have the date appended at the end to avoid overwriting."
                    )
                );
            }

            DebugBreak.boolValue = EditorGUILayout.Toggle
            (
                new GUIContent
                (
                    text: "Debug Break",
                    tooltip: "If true, it pauses the editor"
                ),
                DebugBreak.boolValue
            );

            EditorGUILayout.PropertyField( UnityEvents );

            EditorGUIUtility.labelWidth = defaultLabelWidth;
            EditorGUIUtility.fieldWidth = defaultFieldWidth;

            serializedObject.ApplyModifiedProperties();
        }

        #endregion

        #region Methods -> Private

        private void SetDefaultDebugPacketValues( SerializedProperty debugPacketSerializedProperty )
        {
            GraphyDebugger.DebugPacket debugPacket = new GraphyDebugger.DebugPacket();

            debugPacketSerializedProperty.GetArrayElementAtIndex( debugPacketSerializedProperty.arraySize - 1 )
                .FindPropertyRelative( "Active" )
                .boolValue = debugPacket.Active;

            debugPacketSerializedProperty.GetArrayElementAtIndex( debugPacketSerializedProperty.arraySize - 1 )
                .FindPropertyRelative( "Id" )
                .intValue = debugPacketSerializedProperty.arraySize;

            debugPacketSerializedProperty.GetArrayElementAtIndex( debugPacketSerializedProperty.arraySize - 1 )
                .FindPropertyRelative( "ExecuteOnce" )
                .boolValue = debugPacket.ExecuteOnce;

            debugPacketSerializedProperty.GetArrayElementAtIndex( debugPacketSerializedProperty.arraySize - 1 )
                .FindPropertyRelative( "InitSleepTime" )
                .floatValue = debugPacket.InitSleepTime;

            debugPacketSerializedProperty.GetArrayElementAtIndex( debugPacketSerializedProperty.arraySize - 1 )
                .FindPropertyRelative( "ExecuteSleepTime" )
                .floatValue = debugPacket.ExecuteSleepTime;
        }

        private string GetComparerStringFromDebugVariable( GraphyDebugger.DebugVariable debugVariable )
        {
            switch( debugVariable )
            {
                case GraphyDebugger.DebugVariable.Fps:
                    return "FPS Current";
                case GraphyDebugger.DebugVariable.Fps_Min:
                    return "FPS Min";
                case GraphyDebugger.DebugVariable.Fps_Max:
                    return "FPS Max";
                case GraphyDebugger.DebugVariable.Fps_Avg:
                    return "FPS Avg";

                case GraphyDebugger.DebugVariable.Ram_Allocated:
                    return "Ram Allocated";
                case GraphyDebugger.DebugVariable.Ram_Reserved:
                    return "Ram Reserved";
                case GraphyDebugger.DebugVariable.Ram_Mono:
                    return "Ram Mono";

                case GraphyDebugger.DebugVariable.Audio_DB:
                    return "Audio DB";

                default:
                    return null;
            }
        }

        private string GetComparerStringFromDebugComparer( GraphyDebugger.DebugComparer debugComparer )
        {
            switch( debugComparer )
            {
                case GraphyDebugger.DebugComparer.Less_than:
                    return "<";
                case GraphyDebugger.DebugComparer.Equals_or_less_than:
                    return "<=";
                case GraphyDebugger.DebugComparer.Equals:
                    return "==";
                case GraphyDebugger.DebugComparer.Equals_or_greater_than:
                    return ">=";
                case GraphyDebugger.DebugComparer.Greater_than:
                    return ">";

                default:
                    return null;
            }
        }

        #endregion
    }
}