using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;

namespace UnityEditor.Timeline
{
    partial class TimelineWindow
    {
        static readonly GUIContent[] k_TimeReferenceGUIContents =
        {
            EditorGUIUtility.TrTextContent("Local", "Display time based on the current timeline."),
            EditorGUIUtility.TrTextContent("Global", "Display time based on the master timeline.")
        };

        TimelineMarkerHeaderGUI m_MarkerHeaderGUI;

        void MarkerHeaderGUI()
        {
            var timelineAsset = state.editSequence.asset;
            if (timelineAsset == null)
                return;

            if (m_MarkerHeaderGUI == null)
                m_MarkerHeaderGUI = new TimelineMarkerHeaderGUI(timelineAsset, state);
            m_MarkerHeaderGUI.Draw(markerHeaderRect, markerContentRect, state);
        }

        void DrawTransportToolbar()
        {
            using (new EditorGUI.DisabledScope(currentMode.PreviewState(state) == TimelineModeGUIState.Disabled))
            {
                PreviewModeButtonGUI();
            }

            using (new EditorGUI.DisabledScope(currentMode.ToolbarState(state) == TimelineModeGUIState.Disabled))
            {
                GotoBeginingSequenceGUI();
                PreviousEventButtonGUI();
                PlayButtonGUI();
                NextEventButtonGUI();
                GotoEndSequenceGUI();
                PlayRangeButtonGUI();
                TimeCodeGUI();
                ReferenceTimeGUI();
            }
        }

        void PreviewModeButtonGUI()
        {
            if (state.ignorePreview && !Application.isPlaying)
            {
                GUILayout.Label(DirectorStyles.previewDisabledContent, DirectorStyles.Instance.previewButtonDisabled);
                return;
            }

            EditorGUI.BeginChangeCheck();
            var enabled = state.previewMode;
            enabled = GUILayout.Toggle(enabled, DirectorStyles.previewContent, EditorStyles.toolbarButton);
            if (EditorGUI.EndChangeCheck())
            {
                // turn off auto play as well, so it doesn't auto reenable
                if (!enabled)
                {
                    state.SetPlaying(false);
                    state.recording = false;
                }

                state.previewMode = enabled;

                // if we are successfully enabled, rebuild the graph so initial states work correctly
                // Note: testing both values because previewMode setter can "fail"
                if (enabled && state.previewMode)
                    state.rebuildGraph = true;
            }
        }

        void GotoBeginingSequenceGUI()
        {
            if (GUILayout.Button(DirectorStyles.gotoBeginingContent, EditorStyles.toolbarButton))
            {
                state.editSequence.time = 0;
                state.EnsurePlayHeadIsVisible();
            }
        }

        // in the editor the play button starts/stops simulation
        void PlayButtonGUIEditor()
        {
            EditorGUI.BeginChangeCheck();
            var isPlaying = GUILayout.Toggle(state.playing, DirectorStyles.playContent, EditorStyles.toolbarButton);
            if (EditorGUI.EndChangeCheck())
            {
                state.SetPlaying(isPlaying);
            }
        }

        // in playmode the button reflects the playing state.
        //  needs to disabled if playing is not possible
        void PlayButtonGUIPlayMode()
        {
            bool buttonEnabled = state.masterSequence.director != null &&
                state.masterSequence.director.isActiveAndEnabled;
            using (new EditorGUI.DisabledScope(!buttonEnabled))
            {
                PlayButtonGUIEditor();
            }
        }

        void PlayButtonGUI()
        {
            if (!Application.isPlaying)
                PlayButtonGUIEditor();
            else
                PlayButtonGUIPlayMode();
        }

        void NextEventButtonGUI()
        {
            if (GUILayout.Button(DirectorStyles.nextFrameContent, EditorStyles.toolbarButton))
            {
                state.referenceSequence.frame += 1;
            }
        }

        void PreviousEventButtonGUI()
        {
            if (GUILayout.Button(DirectorStyles.previousFrameContent, EditorStyles.toolbarButton))
            {
                state.referenceSequence.frame -= 1;
            }
        }

        void GotoEndSequenceGUI()
        {
            if (GUILayout.Button(DirectorStyles.gotoEndContent, EditorStyles.toolbarButton))
            {
                state.editSequence.time = state.editSequence.asset.duration;
                state.EnsurePlayHeadIsVisible();
            }
        }

        void PlayRangeButtonGUI()
        {
            using (new EditorGUI.DisabledScope(state.ignorePreview || state.IsEditingASubTimeline()))
            {
                state.playRangeEnabled = GUILayout.Toggle(state.playRangeEnabled, DirectorStyles.Instance.playrangeContent, EditorStyles.toolbarButton);
            }
        }

        void AddButtonGUI()
        {
            if (currentMode.trackOptionsState.newButton == TimelineModeGUIState.Hidden)
                return;

            using (new EditorGUI.DisabledScope(currentMode.trackOptionsState.newButton == TimelineModeGUIState.Disabled))
            {
                if (EditorGUILayout.DropdownButton(DirectorStyles.newContent, FocusType.Passive, EditorStyles.toolbarPopup))
                {
                    // if there is 1 and only 1 track selected, AND it's a group, add to that group
                    var groupTracks = SelectionManager.SelectedTracks().ToList();
                    if (groupTracks.Any(x => x.GetType() != typeof(GroupTrack) || x.lockedInHierarchy))
                        groupTracks = null;

                    SequencerContextMenu.ShowNewTracksContextMenu(groupTracks, state, EditorGUILayout.s_LastRect);
                }
            }
        }

        void ShowMarkersButton()
        {
            var asset = state.editSequence.asset;
            if (asset == null)
                return;

            var content = state.showMarkerHeader ? DirectorStyles.showMarkersOn : DirectorStyles.showMarkersOff;
            SetShowMarkerHeader(GUILayout.Toggle(state.showMarkerHeader, content, DirectorStyles.Instance.showMarkersBtn));
        }

        internal void SetShowMarkerHeader(bool newValue)
        {
            if (state.showMarkerHeader == newValue)
                return;

            TimelineUndo.PushUndo(state.editSequence.viewModel, "Toggle Show Markers");
            state.editSequence.viewModel.showMarkerHeader = newValue;
            if (!newValue)
            {
                var asset = state.editSequence.asset;
                if (asset != null && asset.markerTrack != null)
                {
                    SelectionManager.Remove(asset.markerTrack);
                    foreach (var marker in asset.markerTrack.GetMarkers())
                    {
                        SelectionManager.Remove(marker);
                    }
                }
            }
        }

        static void EditModeToolbarGUI(TimelineMode mode)
        {
            using (new EditorGUI.DisabledScope(mode.EditModeButtonsState(instance.state) == TimelineModeGUIState.Disabled))
            {
                var editType = EditMode.editType;

                EditorGUI.BeginChangeCheck();
                var mixIcon = editType == EditMode.EditType.Mix ? DirectorStyles.mixOn : DirectorStyles.mixOff;
                GUILayout.Toggle(editType == EditMode.EditType.Mix, mixIcon, DirectorStyles.Instance.editModeBtn);
                if (EditorGUI.EndChangeCheck())
                    EditMode.editType = EditMode.EditType.Mix;

                EditorGUI.BeginChangeCheck();
                var rippleIcon = editType == EditMode.EditType.Ripple ? DirectorStyles.rippleOn : DirectorStyles.rippleOff;
                GUILayout.Toggle(editType == EditMode.EditType.Ripple, rippleIcon, DirectorStyles.Instance.editModeBtn);
                if (EditorGUI.EndChangeCheck())
                    EditMode.editType = EditMode.EditType.Ripple;

                EditorGUI.BeginChangeCheck();
                var replaceIcon = editType == EditMode.EditType.Replace ? DirectorStyles.replaceOn : DirectorStyles.replaceOff;
                GUILayout.Toggle(editType == EditMode.EditType.Replace, replaceIcon, DirectorStyles.Instance.editModeBtn);
                if (EditorGUI.EndChangeCheck())
                    EditMode.editType = EditMode.EditType.Replace;
            }
        }

        // Draws the box to enter the time field
        void TimeCodeGUI()
        {
            const string timeFieldHint = "TimelineWindow-TimeCodeGUI";

            EditorGUI.BeginChangeCheck();

            var currentTime = state.editSequence.asset != null ? TimeReferenceUtility.ToTimeString(state.editSequence.time, "F1") : "0";
            var r = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.toolbarTextField, GUILayout.Width(WindowConstants.timeCodeWidth));
            var id = GUIUtility.GetControlID(timeFieldHint.GetHashCode(), FocusType.Keyboard, r);
            var newCurrentTime = EditorGUI.DelayedTextFieldInternal(r, id, GUIContent.none, currentTime, null, EditorStyles.toolbarTextField);

            if (EditorGUI.EndChangeCheck())
                state.editSequence.time = TimeReferenceUtility.FromTimeString(newCurrentTime);
        }

        void ReferenceTimeGUI()
        {
            if (!state.IsEditingASubTimeline())
                return;

            EditorGUI.BeginChangeCheck();
            state.timeReferenceMode = (TimeReferenceMode)EditorGUILayout.CycleButton((int)state.timeReferenceMode, k_TimeReferenceGUIContents, DirectorStyles.Instance.timeReferenceButton);
            if (EditorGUI.EndChangeCheck())
                OnTimeReferenceModeChanged();
        }

        void OnTimeReferenceModeChanged()
        {
            m_TimeAreaDirty = true;
            InitTimeAreaFrameRate();
            SyncTimeAreaShownRange();

            foreach (var inspector in InspectorWindow.GetAllInspectorWindows())
            {
                inspector.Repaint();
            }
        }

        void DrawHeaderEditButtons()
        {
            if (state.editSequence.asset == null)
                return;

            using (new GUILayout.HorizontalScope(EditorStyles.toolbar, GUILayout.Width(sequenceHeaderRect.width)))
            {
                GUILayout.Space(DirectorStyles.kBaseIndent);
                AddButtonGUI();
                GUILayout.FlexibleSpace();
                EditModeToolbarGUI(currentMode);
                ShowMarkersButton();
                EditorGUILayout.Space();
            }
        }
    }
}
