using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Timeline.Actions;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
    /// 
    /// Information currently being edited in the Timeline Editor Window.
    /// 
    public static class TimelineEditor
    {
        /// 
        /// Returns a reference to the Timeline Window.
        /// 
        /// A reference to the TimelineWindow and null if the window is not opened.
        public static TimelineEditorWindow GetWindow()
        {
            return window;
        }
        /// 
        /// Returns a reference to the Timeline Window. If the window is not opened, it will be opened.
        /// 
        /// A reference to the TimelineWindow.
        public static TimelineEditorWindow GetOrCreateWindow()
        {
            if (window != null)
                return window;
            return EditorWindow.GetWindow(false, null, false);
        }
        /// 
        /// The PlayableDirector associated with the timeline currently being shown in the Timeline window.
        /// 
        public static PlayableDirector inspectedDirector => state?.editSequence.director;
        /// 
        /// The PlayableDirector responsible for the playback of the timeline currently being shown in the Timeline window.
        /// 
        public static PlayableDirector masterDirector => state?.masterSequence.director;
        /// 
        /// The TimelineAsset currently being shown in the Timeline window.
        /// 
        public static TimelineAsset inspectedAsset => state?.editSequence.asset;
        /// 
        /// The TimelineAsset at the root of the hierarchy currently being shown in the Timeline window.
        /// 
        public static TimelineAsset masterAsset => state?.masterSequence.asset;
        /// 
        /// The PlayableDirector currently being shown in the Timeline Editor Window.
        /// 
        [Obsolete("playableDirector is ambiguous. Please select either inspectedDirector or masterDirector instead.", false)]
        public static PlayableDirector playableDirector
        {
            get { return inspectedDirector; }
        }
        /// 
        /// The TimelineAsset currently being shown in the Timeline Editor Window.
        /// 
        [Obsolete("timelineAsset is ambiguous. Please select either inspectedAsset or masterAsset instead.", false)]
        public static TimelineAsset timelineAsset
        {
            get { return inspectedAsset; }
        }
        /// 
        /// 
        /// Refreshes the different components affected by the currently inspected
        /// , based on the  provided.
        /// 
        /// 
        /// For better performance, it is recommended that you invoke this method once, after you modify the
        /// . You should also combine reasons using the | operator.
        /// 
        /// 
        /// 
        /// Note: This operation is not synchronous. It is performed during the next GUI loop.
        /// 
        /// The reason why a refresh should be performed.
        public static void Refresh(RefreshReason reason)
        {
            if (state == null)
                return;
            if ((reason & RefreshReason.ContentsAddedOrRemoved) != 0)
            {
                state.Refresh();
            }
            else if ((reason & RefreshReason.ContentsModified) != 0)
            {
                state.rebuildGraph = true;
            }
            else if ((reason & RefreshReason.SceneNeedsUpdate) != 0)
            {
                state.Evaluate();
            }
            window.Repaint();
        }
        internal static TimelineWindow window => TimelineWindow.instance;
        internal static WindowState state => window == null ? null : window.state;
        internal static readonly Clipboard clipboard = new Clipboard();
        /// 
        /// The list of clips selected in the TimelineEditor.
        /// 
        public static TimelineClip[] selectedClips
        {
            get { return Selection.GetFiltered(SelectionMode.Unfiltered).Select(e => e.clip).Where(x => x != null).ToArray(); }
            set
            {
                if (value == null || value.Length == 0)
                {
                    Selection.objects = null;
                }
                else
                {
                    var objects = new List();
                    foreach (var clip in value)
                    {
                        if (clip == null)
                            continue;
                        var editorClip = EditorClipFactory.GetEditorClip(clip);
                        if (editorClip != null)
                            objects.Add(editorClip);
                    }
                    Selection.objects = objects.ToArray();
                }
            }
        }
        /// 
        /// The clip selected in the TimelineEditor.
        /// 
        /// 
        /// If there are multiple clips selected, this property returns the first clip.
        /// 
        public static TimelineClip selectedClip
        {
            get
            {
                var editorClip = Selection.activeObject as EditorClip;
                if (editorClip != null)
                    return editorClip.clip;
                return null;
            }
            set
            {
                var editorClip = (value != null) ? EditorClipFactory.GetEditorClip(value) : null;
                Selection.activeObject = editorClip;
            }
        }
        /// 
        /// Local time (in seconds) of the inspected sequence.
        /// 
        /// Thrown if timeline window is not available.
        internal static double inspectedSequenceTime
        {
            get => state?.editSequence.time ?? 0;
            set
            {
                if (state == null)
                    throw new InvalidOperationException("Cannot set time. Timeline Window may not be available.");
                state.editSequence.time = value;
            }
        }
        /// 
        /// Global time (in seconds) of the master timeline.
        /// Same as local time if not inspected a subtimeline.
        /// 
        /// Thrown if timeline window is not available.
        internal static double masterSequenceTime
        {
            get => state?.editSequence.ToGlobalTime(state.editSequence.time) ?? 0;
            set
            {
                if (state == null)
                    throw new InvalidOperationException("Cannot set time. Timeline Window may not be available.");
                state.masterSequence.time = value;
            }
        }
        /// 
        /// Visible time range (in seconds) in Editor.
        /// x : min time
        /// y : max time
        /// 
        /// Thrown if timeline window is not available.
        internal static Vector2 visibleTimeRange
        {
            get => state?.timeAreaShownRange ?? TimelineAssetViewModel.TimeAreaDefaultRange;
            set
            {
                if (state == null)
                    throw new InvalidOperationException("Cannot set visible time range. Timeline Window may not be available.");
                state.timeAreaShownRange = value;
            }
        }
        internal static ActionContext CurrentContext(Vector2? mousePos = null)
        {
            return new ActionContext
            {
                invocationTime = mousePos != null ? TimelineHelpers.GetCandidateTime(mousePos) : (double?)null,
                clips = SelectionManager.SelectedClips(),
                tracks = SelectionManager.SelectedTracks(),
                markers = SelectionManager.SelectedMarkers(),
                timeline = inspectedAsset,
                director = inspectedDirector
            };
        }
        /// 
        /// Converts time from the master timeline to the current inspected timeline.
        /// 
        /// Time in the referential of the main timeline
        /// Time in the referential of the sub-timeline that is currently show.
        /// Returns  if there is no sub-timeline or if no timeline is shown.
        public static double GetInspectedTimeFromMasterTime(double masterTime)
        {
            ISequenceState editSequence = state?.editSequence;
            if (editSequence == null)
                return masterTime;
            return state.editSequence.ToLocalTime(masterTime);
        }
        /// 
        /// Converts time from the current inspected timeline to the master timeline.
        /// 
        /// Time in the referential of the sub-timeline
        /// Time in the referential of the main timeline.
        /// Returns  if there if no timeline is shown.
        public static double GetMasterTimeFromInspectedTime(double inspectedTime)
        {
            ISequenceState editSequence = state?.editSequence;
            if (editSequence == null)
                return inspectedTime;
            return editSequence.ToGlobalTime(inspectedTime);
        }
        internal static void RefreshPreviewPlay()
        {
            if (state == null || !state.playing)
                return;
            state.Pause();
            state.Play();
        }
    }
    /// 
    ///  uses these flags to determine what needs to be refreshed or updated.
    /// 
    /// 
    /// Use the | operator to combine flags.
    /// 
    /// 
    ///