using System;
using UnityEngine.Playables;
using UnityEngine.SceneManagement;
namespace UnityEngine.Timeline
{
    /// 
    /// Playable that controls and instantiates a Prefab.
    /// 
    public class PrefabControlPlayable : PlayableBehaviour
    {
        GameObject m_Instance;
#if UNITY_EDITOR
        private bool m_IsActiveCached;
#endif
        /// 
        /// Creates a Playable with a PrefabControlPlayable behaviour attached
        /// 
        /// The PlayableGraph to inject the Playable into.
        /// The prefab to instantiate from
        /// Transform to parent instance to. Can be null.
        /// Returns a Playabe with PrefabControlPlayable behaviour attached.
        public static ScriptPlayable Create(PlayableGraph graph, GameObject prefabGameObject, Transform parentTransform)
        {
            if (prefabGameObject == null)
                return ScriptPlayable.Null;
            var handle = ScriptPlayable.Create(graph);
            handle.GetBehaviour().Initialize(prefabGameObject, parentTransform);
            return handle;
        }
        /// 
        /// The instance of the prefab created by this behaviour
        /// 
        public GameObject prefabInstance
        {
            get { return m_Instance; }
        }
        /// 
        /// Initializes the behaviour with a prefab and parent transform
        /// 
        /// The prefab to instantiate from
        /// Transform to parent instance to. Can be null.
        /// The created instance
        public GameObject Initialize(GameObject prefabGameObject, Transform parentTransform)
        {
            if (prefabGameObject == null)
                throw new ArgumentNullException("Prefab cannot be null");
            if (m_Instance != null)
            {
                Debug.LogWarningFormat("Prefab Control Playable ({0}) has already been initialized with a Prefab ({1}).", prefabGameObject.name, m_Instance.name);
            }
            else
            {
#if UNITY_EDITOR
                if (!Application.isPlaying)
                {
                    m_Instance = (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(prefabGameObject, parentTransform);
                    UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabUpdated;
                }
                else
#endif
                {
                    m_Instance = Object.Instantiate(prefabGameObject, parentTransform, false);
                }
                m_Instance.name = prefabGameObject.name + " [Timeline]";
                m_Instance.SetActive(false);
                SetHideFlagsRecursive(m_Instance);
            }
            return m_Instance;
        }
        /// 
        /// This function is called when the Playable that owns the PlayableBehaviour is destroyed.
        /// 
        /// The playable this behaviour is attached to.
        public override void OnPlayableDestroy(Playable playable)
        {
            if (m_Instance)
            {
                if (Application.isPlaying)
                    Object.Destroy(m_Instance);
                else
                    Object.DestroyImmediate(m_Instance);
            }
#if UNITY_EDITOR
            UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated;
#endif
        }
        /// 
        /// This function is called when the Playable play state is changed to Playables.PlayState.Playing.
        /// 
        /// The Playable that owns the current PlayableBehaviour.
        /// A FrameData structure that contains information about the current frame context.
        public override void OnBehaviourPlay(Playable playable, FrameData info)
        {
            if (m_Instance == null)
                return;
            m_Instance.SetActive(true);
#if UNITY_EDITOR
            m_IsActiveCached = true;
#endif
        }
        /// 
        /// This function is called when the Playable play state is changed to PlayState.Paused.
        /// 
        /// The playable this behaviour is attached to.
        /// A FrameData structure that contains information about the current frame context.
        public override void OnBehaviourPause(Playable playable, FrameData info)
        {
            // OnBehaviourPause can be called if the graph is stopped for a variety of reasons
            //  the effectivePlayState will test if the pause is due to the clip being out of bounds
            if (m_Instance != null && info.effectivePlayState == PlayState.Paused)
            {
                m_Instance.SetActive(false);
#if UNITY_EDITOR
                m_IsActiveCached = false;
#endif
            }
        }
#if UNITY_EDITOR
        void OnPrefabUpdated(GameObject go)
        {
            if (go == m_Instance)
            {
                SetHideFlagsRecursive(go);
                go.SetActive(m_IsActiveCached);
            }
        }
#endif
        static void SetHideFlagsRecursive(GameObject gameObject)
        {
            if (gameObject == null)
                return;
            gameObject.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
            if (!Application.isPlaying)
                gameObject.hideFlags |= HideFlags.HideInHierarchy;
            foreach (Transform child in gameObject.transform)
            {
                SetHideFlagsRecursive(child.gameObject);
            }
        }
    }
}