/**
 * 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 Live2D.Cubism.Core;
using System;
using UnityEngine;
namespace Live2D.Cubism.Framework.Physics
{
    /// 
    /// Output data of physics.
    /// 
    [Serializable]
    public struct CubismPhysicsOutput
    {
        /// 
        /// Delegation of function of getting output value.
        /// 
        /// Translation.
        /// Parameter.
        /// Particles.
        /// Index of particle.
        /// Gravity.
        /// Output value.
        public delegate float ValueGetter(
            Vector2 translation,
            CubismParameter parameter,
            CubismPhysicsParticle[] particles,
            int particleIndex,
            Vector2 gravity
        );
        /// 
        /// Delegation of function of getting output scale.
        /// 
        /// Output scale.
        public delegate float ScaleGetter();
        /// 
        /// Gets output for translation X-axis.
        /// 
        /// Translation.
        /// Parameter.
        /// Particles.
        /// Index of particle.
        /// Gravity.
        /// Output value.
        private float GetOutputTranslationX(
            Vector2 translation,
            CubismParameter parameter,
            CubismPhysicsParticle[] particles,
            int particleIndex,
            Vector2 gravity
        )
        {
            var outputValue = translation.x;
            if (IsInverted)
            {
                outputValue *= -1.0f;
            }
            return outputValue;
        }
        /// 
        /// Gets output for translation Y-axis.
        /// 
        /// Translation.
        /// Parameter.
        /// Particles.
        /// Index of particle.
        /// Gravity.
        /// Output value.
        private float GetOutputTranslationY(
            Vector2 translation,
            CubismParameter parameter,
            CubismPhysicsParticle[] particles,
            int particleIndex,
            Vector2 gravity
        )
        {
            var outputValue = translation.y;
            if (IsInverted)
            {
                outputValue *= -1.0f;
            }
            return outputValue;
        }
        /// 
        /// Gets output for angle.
        /// 
        /// Translation.
        /// Parameter.
        /// Particles.
        /// Index of particle.
        /// Gravity.
        /// Output value.
        private float GetOutputAngle(
            Vector2 translation,
            CubismParameter parameter,
            CubismPhysicsParticle[] particles,
            int particleIndex,
            Vector2 gravity
        )
        {
            var parentGravity = Vector2.zero;
            if (CubismPhysics.UseAngleCorrection)
            {
                if (particleIndex < 2)
                {
                    parentGravity = gravity;
                    parentGravity.y *= -1.0f;
                }
                else
                {
                    parentGravity = particles[particleIndex - 1].Position -
                        particles[particleIndex - 2].Position;
                }
            }
            else
            {
                parentGravity = gravity;
                parentGravity.y *= -1.0f;
            }
            var outputValue = CubismPhysicsMath.DirectionToRadian(parentGravity, translation);
            if (IsInverted)
            {
                outputValue *= -1.0f;
            }
            return outputValue;
        }
        /// 
        /// Gets output scale for translation X-axis.
        /// 
        /// Output scale.
        private float GetOutputScaleTranslationX()
        {
            return TranslationScale.x;
        }
        /// 
        /// Gets output scale for translation Y-axis.
        /// 
        /// Output scale.
        private float GetOutputScaleTranslationY()
        {
            return TranslationScale.y;
        }
        /// 
        /// Gets output scale for angle.
        /// 
        /// Output scale.
        private float GetOutputScaleAngle()
        {
            return AngleScale;
        }
        public void InitializeGetter()
        {
            switch (SourceComponent)
            {
                case CubismPhysicsSourceComponent.X:
                    {
                        GetScale =
                            GetOutputScaleTranslationX;
                        GetValue =
                            GetOutputTranslationX;
                    }
                    break;
                case CubismPhysicsSourceComponent.Y:
                    {
                        GetScale =
                            GetOutputScaleTranslationY;
                        GetValue =
                            GetOutputTranslationY;
                    }
                    break;
                case CubismPhysicsSourceComponent.Angle:
                    {
                        GetScale =
                            GetOutputScaleAngle;
                        GetValue =
                            GetOutputAngle;
                    }
                    break;
            }
        }
        /// 
        /// Parameter ID of destination.
        /// 
        [SerializeField]
        public string DestinationId;
        /// 
        /// Index of particle.
        /// 
        [SerializeField]
        public int ParticleIndex;
        /// 
        /// Scale of transition.
        /// 
        [SerializeField]
        public Vector2 TranslationScale;
        /// 
        /// Scale of angle.
        /// 
        [SerializeField]
        public float AngleScale;
        /// 
        /// Weight.
        /// 
        [SerializeField]
        public float Weight;
        /// 
        /// Component of source.
        /// 
        [SerializeField]
        public CubismPhysicsSourceComponent SourceComponent;
        /// 
        /// True if value is inverted; otherwise.
        /// 
        [SerializeField]
        public bool IsInverted;
        /// 
        /// The value that below minimum.
        /// 
        [NonSerialized]
        public float ValueBelowMinimum;
        /// 
        /// The value that exceeds maximum.
        /// 
        [NonSerialized]
        public float ValueExceededMaximum;
        /// 
        /// Destination data from parameter.
        /// 
        [NonSerialized]
        public CubismParameter Destination;
        /// 
        /// Function of getting output value.
        /// 
        [NonSerialized]
        public ValueGetter GetValue;
        /// 
        /// Function of getting output scale.
        /// 
        [NonSerialized]
        public ScaleGetter GetScale;
    }
}