/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations;
namespace Live2D.Cubism.Framework.MotionFade
{
    public class CubismFadeStateObserver : StateMachineBehaviour, ICubismFadeState
    {
        #region variable
        /// 
        /// Cubism fade motion list.
        /// 
        private CubismFadeMotionList _cubismFadeMotionList;
        /// 
        /// Cubism playing motion list.
        /// 
        private List _playingMotions;
        /// 
        /// State that attached this is default.
        /// 
        private bool _isDefaulState;
        /// 
        /// Layer index that attached this.
        /// 
        private int _layerIndex;
        /// 
        /// Weight of layer that attached this.
        /// 
        private float _layerWeight;
        /// 
        /// State that attached this is transition finished.
        /// 
        private bool _isStateTransitionFinished;
        #endregion
        #region Fade State Interface
        /// 
        /// Get cubism playing motion list.
        /// 
        /// Cubism playing motion list.
        public List GetPlayingMotions()
        {
            return _playingMotions;
        }
        /// 
        /// Is default state.
        /// 
        ///  State is default;  otherwise.
        public bool IsDefaultState()
        {
            return _isDefaulState;
        }
        /// 
        /// Get layer weight.
        /// 
        /// Layer weight.
        public float GetLayerWeight()
        {
            return _layerWeight;
        }
        /// 
        /// Get state transition finished.
        /// 
        ///  State transition is finished;  otherwise.
        public bool GetStateTransitionFinished()
        {
            return _isStateTransitionFinished;
        }
        /// 
        /// Set state transition finished.
        /// 
        /// State is finished.
        public void SetStateTransitionFinished(bool isFinished)
        {
            _isStateTransitionFinished = isFinished;
        }
        /// 
        /// Stop animation.
        /// 
        /// Playing motion index.
        public void StopAnimation(int index)
        {
            _playingMotions.RemoveAt(index);
        }
        #endregion
        #region Unity Event Handling
        /// 
        /// Called by Unity.
        /// 
        private void OnEnable()
        {
            _isStateTransitionFinished = false;
            if (_playingMotions == null)
            {
                _playingMotions = new List();
            }
        }
        /// 
        /// Called by Unity.
        /// 
        /// Animator.
        /// Animator state info.
        /// Index of the layer.
        /// Animation controller playable.
        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
        {
            var fadeController = animator.gameObject.GetComponent();
            // Fail silently...
            if (fadeController == null)
            {
                return;
            }
            _cubismFadeMotionList = fadeController.CubismFadeMotionList;
            _layerIndex = layerIndex;
            _layerWeight = (_layerIndex == 0)
                ? 1.0f
                : animator.GetLayerWeight(_layerIndex);
            var animatorClipInfo = controller.GetNextAnimatorClipInfo(layerIndex);
            _isDefaulState = (animatorClipInfo == null || animatorClipInfo.Length == 0);
            if (_isDefaulState)
            {
                // Get the motion of Default State only for the first time.
                animatorClipInfo = controller.GetCurrentAnimatorClipInfo(layerIndex);
            }
            // Set playing motions end time.
            if ((_playingMotions.Count > 0) && (_playingMotions[_playingMotions.Count - 1].Motion != null))
            {
                var motion = _playingMotions[_playingMotions.Count - 1];
                var time = Time.time;
                var newEndTime = time + motion.Motion.FadeOutTime;
                motion.EndTime = newEndTime;
                while (motion.IsLooping)
                {
                    if ((motion.StartTime + motion.Motion.MotionLength) >= time)
                    {
                        break;
                    }
                    motion.StartTime += motion.Motion.MotionLength;
                }
                _playingMotions[_playingMotions.Count - 1] = motion;
            }
            for (var i = 0; i < animatorClipInfo.Length; ++i)
            {
                CubismFadePlayingMotion playingMotion;
                var instanceId = -1;
                var events = animatorClipInfo[i].clip.events;
                for (var k = 0; k < events.Length; ++k)
                {
                    if (events[k].functionName != "InstanceId")
                    {
                        continue;
                    }
                    instanceId = events[k].intParameter;
                    break;
                }
                var motionIndex = -1;
                for (var j = 0; j < _cubismFadeMotionList.MotionInstanceIds.Length; ++j)
                {
                    if (_cubismFadeMotionList.MotionInstanceIds[j] != instanceId)
                    {
                        continue;
                    }
                    motionIndex = j;
                    break;
                }
                playingMotion.Motion = (motionIndex == -1)
                    ? null
                    : _cubismFadeMotionList.CubismFadeMotionObjects[motionIndex];
                playingMotion.Speed = 1.0f;
                playingMotion.StartTime = Time.time;
                playingMotion.FadeInStartTime = Time.time;
                if(playingMotion.Motion == null)
                {
                    continue;
                }
                playingMotion.EndTime = (playingMotion.Motion.MotionLength <= 0)
                                        ? -1
                                        : playingMotion.StartTime + playingMotion.Motion.MotionLength;
                playingMotion.IsLooping = animatorClipInfo[i].clip.isLooping;
                playingMotion.Weight = 0.0f;
                _playingMotions.Add(playingMotion);
            }
        }
        /// 
        /// Called by Unity.
        /// 
        /// Animator.
        /// Animator state info.
        /// Index of the layer.
        public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            _isStateTransitionFinished = true;
        }
        #endregion
    }
}