using System;
using UnityEngine;

namespace pdinklag.CPW.Common
{
    /// <summary>
    /// Represents the playback of an animation.
    /// </summary>
    public interface IAnimPlayback : ITargetContext
    {
        /// <summary>
        /// The wrestler that is playing this animation.
        /// </summary>
        IWrestler Wrestler { get; }

        /// <summary>
        /// The total duration of the playback.
        /// </summary>
        float Duration { get; }

        /// <summary>
        /// The current normalized time of the animation playback.
        /// </summary>
        float Time { get; }

        /// <summary>
        /// The current normalized time (between 0 and 1) of the animation playback.
        /// </summary>
        float NormalizedTime { get; }

        /// <summary>
        /// The playback speed multiplier (1 is original, less means faster, more means slower).
        /// </summary>
        /// <value></value>
        float Speed { get; set; }

        /// <summary>
        /// Provides the context for this animation.
        /// </summary>
        IAnimContext Context { get; }

        /// <summary>
        /// Adds an IK effector controller for this animation that matches a body POI to a position.
        /// 
        /// The weight is retrieved from the IKEffectorWeights component that should be animated in the clip.
        /// </summary>
        /// <param name="effector">the IK effector</param>
        /// <param name="poi">the POI that should be matched</param>
        /// <param name="pos">the position at which the POI should be matched</param>
        IIKEffectorController AddIKEffector(IKEffector effector, BodyPOI poi, ITarget pos);

        /// <summary>
        /// Adds an IK effector controller for this animation that matches a body POI to a position.
        /// 
        /// The weight is retrieved from the given weight curve.
        /// </summary>
        /// <param name="effector">the IK effector</param>
        /// <param name="poi">the POI that should be matched</param>
        /// <param name="pos">the position at which the POI should be matched</param>
        /// <param name="weightCurve">the weight curve over the animation's normalized time (0 to 1)</param>
        IIKEffectorController AddIKEffector(IKEffector effector, BodyPOI poi, ITarget pos, AnimControlCurve weightCurve);

        /// <summary>
        /// Adds an IK effector controller for this animation that snaps the POI to the ground.
        /// 
        /// The weight is computed from the POI's distance from the ground in the animation.
        /// </summary>
        /// <param name="effector">the IK effector</param>
        /// <param name="poi">the POI that should be grounded</param>
        IIKEffectorController AddGroundSnap(IKEffector effector, BodyPOI poi, float sampleAt = float.NaN);

        /// <summary>
        /// Clears all IK effectors.
        /// </summary>
        void ClearIKEffectors();

        /// <summary>
        /// Adds a rope grab controller.
        /// 
        /// The rope will be grabbed by the given body part.
        /// </summary>
        /// <param name="rope">the affected rope</param>
        /// <param name="poi">the body part grabbing the rope</param>
        /// <param name="addY">additional Y coordinate shift to apply</param>
        /// <param name="weightCurve">the grab weight curve</param>
        void AddRopeGrab(IRope rope, BodyPOI poi, float addY, AnimControlCurve weightCurve);

        /// <summary>
        /// Adds a rope interactor for the entire body.
        /// </summary>
        void AddRopeInteractor();

        /// <summary>
        /// Adds a rope interactor for a specific body part.
        /// </summary>
        /// <param name="bodyPart">the body part</param>
        void AddRopeInteractor(RagdollPart bodyPart);

        /// <summary>
        /// Adds ragdoll muscle weight control; only works as character is ragdolling.
        /// </summary>
        /// <param name="weightCurve">the muscle weight curve</param>
        void AddRagdollMuscle(AnimControlCurve weightCurve);

        /// <summary>
        /// Adds additional root motion to the animation.
        /// </summary>
        /// <param name="movement">the movement vector</param>
        /// <param name="startTime">the normalized movement start time (0 to 1)</param>
        /// <param name="endTime">the normalized movement end time (0 to 1)</param>
        void AddMotion(Vector3 movement, float startTime = 0.0f, float endTime = 1.0f);

        /// <summary>
        /// Adds additional root rotation to the animation.
        /// </summary>
        /// <param name="rotation">the rotation</param>
        /// <param name="startTime">the normalized movement start time (0 to 1)</param>
        /// <param name="endTime">the normalized movement end time (0 to 1)</param>
        void AddRotation(Quaternion rotation, float startTime = 0.0f, float endTime = 1.0f);

        /// <summary>
        /// Registers a callback for an animation event.
        /// </summary>
        /// <param name="name">the name of the event, which is given as the event's string parameter</param>
        /// <param name="callback">the function to call</param>
        void RegisterCallback(string name, Action callback);

        /// <summary>
        /// Registers a callback for when the animation reaches a certain time.
        /// </summary>
        /// <param name="normalizedTime">the normalized time at which to execute the callback (0 to 1)</param>
        /// <param name="callback">the function to call</param>
        void RegisterCallback(float normalizedTime, Action callback);

        /// <summary>
        /// Sets the animation weight multiplier of a specific ragdoll part (all 1 by default).
        /// Only meaningful if the wrestler is ragdolling.
        /// </summary>
        /// <param name="part">the ragdoll part in question</param>
        /// <param name="weight">the desired animation weight (0 to 1)</param>
        void SetRagdollPartWeight(RagdollPart part, float weight);

        /// <summary>
        /// Samples the current animation at the given time.
        /// </summary>
        /// <param name="relativeTime">the relative time to sample at</param>
        /// <param name="offsetTime">the relative time to sample at initially</param>
        /// <returns>the sampling object</returns>
        IAnimSampling Sample(float relativeTime, float offsetTime = 0);

        /// <summary>
        /// Gets the normalized time of the given animation keyframe.
        /// </summary>
        /// <param name="name">the name of the keyframe, which is given as the event's string parameter</param>
        /// <returns></returns>
        float GetKeyframeTime(string name);

        /// <summary>
        /// Pauses the animation.
        /// </summary>
        void Pause();

        /// <summary>
        /// Waits for the animation playback to finish.
        /// </summary>
        /// <param name="ctx">the execution context</param>
        /// <returns>the corresponding yield instruction</returns>
        YieldInstructionCode Wait(IContext ctx);

        /// <summary>
        /// Waits for the animation playback to reach a certain time.
        /// </summary>
        /// <param name="ctx">the execution context</param>
        /// <param name="normalizedTime">the normalized time at which to execute the callback (0 to 1)</param>
        /// <returns>the corresponding yield instruction</returns>
        YieldInstructionCode WaitUntil(IContext ctx, float normalizedTime);

        /// <summary>
        /// Waits for the given animation event to occur.
        /// </summary>
        /// <param name="ctx">the execution context</param>
        /// <param name="name">the name of the event, which is given as the event's string parameter</param>
        /// <returns>the corresponding yield instruction</returns>
        YieldInstructionCode WaitUntil(IContext ctx, string name);

        /// <summary>
        /// Waits until the last frame of the animation is reached.
        /// </summary>
        /// <param name="ctx">the execution context</param>
        /// <returns>the corresponding yield instruction</returns>
        YieldInstructionCode WaitUntilLastFrame(IContext ctx);
    }
}
