using System;
using System.Collections.Generic;
using UnityEngine.Events;
namespace UnityEngine.XR.Content.Interaction
{
    /// 
    /// Rewinds positional changes made this object and its children to restore it back to a 'complete' object
    /// 
    public class Unbreakable : MonoBehaviour
    {
        [Serializable] public class RestoreEvent : UnityEvent { }
        [SerializeField]
        [Tooltip("How long to wait before rewinding the object's motion.")]
        float m_RestTime = 1.0f;
        [SerializeField]
        [Tooltip("How long to spend restoring the object.")]
        float m_RestoreTime = 2.0f;
        [SerializeField]
        [Tooltip("A 'non broken' object to replace this object with when motion rewinding is complete.")]
        GameObject m_RestoredVersion;
        [SerializeField]
        [Tooltip("Events to fire when the 'non broken' object is restored.")]
        RestoreEvent m_OnRestore = new RestoreEvent();
        bool m_Resting = true;
        float m_Timer = 0.0f;
        bool m_Restored = false;
        struct ChildPoses
        {
            internal Pose m_StartPose;
            internal Pose m_EndPose;
        }
        Dictionary m_ChildPoses = new Dictionary();
        List m_ChildTransforms = new List();
        /// 
        /// Events to fire when the 'non broken' object is restored.
        /// 
        public RestoreEvent onRestore => m_OnRestore;
        void Start()
        {
            // Go through all children
            GetComponentsInChildren(m_ChildTransforms);
            // Cache their start positions
            foreach (var child in m_ChildTransforms)
            {
                m_ChildPoses.Add(child, new ChildPoses { m_StartPose = new Pose(child.position, child.rotation) });
            }
        }
        void Update()
        {
            if (m_Restored)
                return;
            // Phase 1 - wait to rewind
            // Phase 2 - rewind all positions, using a an inverse quadratic curve
            // Phase 3 - replace object, destroy this one
            m_Timer += Time.deltaTime;
            if (m_Resting)
            {
                if (m_Timer > m_RestTime)
                {
                    m_Timer = 0.0f;
                    m_Resting = false;
                    foreach (var child in m_ChildTransforms)
                    {
                        if (child == null)
                            continue;
                        var poses = m_ChildPoses[child];
                        poses.m_EndPose = new Pose(child.position, child.rotation);
                        m_ChildPoses[child] = poses;
                    }
                }
            }
            else
            {
                var timePercent = m_Timer / m_RestoreTime;
                if (timePercent > 1.0f)
                {
                    m_Restored = true;
                    var restoredVersion = Instantiate(m_RestoredVersion, transform.position, transform.rotation);
                    m_OnRestore.Invoke(restoredVersion);
                    Destroy(gameObject);
                }
                else
                {
                    timePercent = 1.0f - ((1.0f - timePercent) * (1.0f - timePercent));
                    foreach (var child in m_ChildTransforms)
                    {
                        if (child == null)
                            continue;
                        var poses = m_ChildPoses[child];
                        var lerpedPosition = Vector3.Lerp(poses.m_EndPose.position, poses.m_StartPose.position, timePercent);
                        var lerpedRotation = Quaternion.Slerp(poses.m_EndPose.rotation, poses.m_StartPose.rotation, timePercent);
                        child.position = lerpedPosition;
                        child.rotation = lerpedRotation;
                    }
                }
            }
        }
    }
}