/**
 * 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.Framework.Physics;
using System;
using UnityEngine;
namespace Live2D.Cubism.Framework.Json
{
    [Serializable]
    public sealed class CubismPhysics3Json
    {
        /// 
        /// Loads a physics3.json asset.
        /// 
        /// physics3.json to deserialize.
        /// Deserialized physics3.json on success;  otherwise.
        public static CubismPhysics3Json LoadFrom(string physics3Json)
        {
            return (string.IsNullOrEmpty(physics3Json))
                ? null
                : JsonUtility.FromJson(physics3Json);
        }
        /// 
        /// Loads a physics3.json asset.
        /// 
        /// motion3.json to deserialize.
        /// Deserialized physics3.json on success;  otherwise.
        public static CubismPhysics3Json LoadFrom(TextAsset physics3JsonAsset)
        {
            return (physics3JsonAsset == null)
                ? null
                : LoadFrom(physics3JsonAsset.text);
        }
        public CubismPhysicsRig ToRig()
        {
            var instance = new CubismPhysicsRig();
            instance.Gravity.x = Meta.EffectiveForces.Gravity.X;
            instance.Gravity.y = Meta.EffectiveForces.Gravity.Y;
            instance.Wind.x = Meta.EffectiveForces.Wind.X;
            instance.Wind.y = Meta.EffectiveForces.Wind.Y;
            instance.SubRigs = new CubismPhysicsSubRig[Meta.PhysicsSettingCount];
            for (var i = 0; i < instance.SubRigs.Length; ++i)
            {
                instance.SubRigs[i] = new CubismPhysicsSubRig
                {
                    Input         = ReadInput(PhysicsSettings[i].Input),
                    Output        = ReadOutput(PhysicsSettings[i].Output),
                    Particles     = ReadParticles(PhysicsSettings[i].Vertices),
                    Normalization = ReadNormalization(PhysicsSettings[i].Normalization)
                };
            }
            return instance;
        }
        private CubismPhysicsInput[] ReadInput(SerializableInput[] source)
        {
            var dataArray = new CubismPhysicsInput[source.Length];
            for (var i = 0; i < dataArray.Length; ++i)
            {
                dataArray[i] = new CubismPhysicsInput
                {
                    SourceId            = source[i].Source.Id,
                    AngleScale          = 0.0f,
                    ScaleOfTranslation  = Vector2.zero,
                    Weight              = source[i].Weight,
                    SourceComponent     = (CubismPhysicsSourceComponent) Enum.Parse(
                        typeof(CubismPhysicsSourceComponent), source[i].Type
                        ),
                    IsInverted          = source[i].Reflect
                };
            }
            return dataArray;
        }
        private CubismPhysicsOutput[] ReadOutput(SerializableOutput[] source)
        {
            var dataArray = new CubismPhysicsOutput[source.Length];
            for (var i = 0; i < dataArray.Length; ++i)
            {
                dataArray[i] = new CubismPhysicsOutput
                {
                    DestinationId        = source[i].Destination.Id,
                    ParticleIndex        = source[i].VertexIndex,
                    TranslationScale     = Vector2.zero,
                    AngleScale           = source[i].Scale,
                    Weight               = source[i].Weight,
                    SourceComponent      = (CubismPhysicsSourceComponent) Enum.Parse(
                        typeof(CubismPhysicsSourceComponent), source[i].Type
                        ),
                    IsInverted           = source[i].Reflect,
                    ValueBelowMinimum    = 0.0f,
                    ValueExceededMaximum = 0.0f
                };
            }
            return dataArray;
        }
        private CubismPhysicsParticle[] ReadParticles(SerializableVertex[] source)
        {
            var dataArray = new CubismPhysicsParticle[source.Length];
            for (var i = 0; i < dataArray.Length; ++i)
            {
                dataArray[i] = new CubismPhysicsParticle
                {
                    InitialPosition =
                    {
                        x = source[i].Position.X,
                        y = source[i].Position.Y
                    },
                    Mobility          = source[i].Mobility,
                    Delay             = source[i].Delay,
                    Acceleration      = source[i].Acceleration,
                    Radius            = source[i].Radius,
                    Position          = Vector2.zero,
                    LastPosition      = Vector2.zero,
                    LastGravity       = Vector2.down,
                    Force             = Vector2.zero,
                    Velocity          = Vector2.zero
                };
            }
            return dataArray;
        }
        private CubismPhysicsNormalization ReadNormalization(SerializableNormalization source)
        {
            return new CubismPhysicsNormalization
            {
                Position =
                {
                    Maximum = source.Position.Maximum,
                    Minimum = source.Position.Minimum,
                    Default = source.Position.Default
                },
                Angle =
                {
                    Maximum = source.Angle.Maximum,
                    Minimum = source.Angle.Minimum,
                    Default = source.Angle.Default
                }
            };
        }
    #region Json Data
        /// 
        /// Json file format version.
        /// 
        [SerializeField]
        public int Version;
        /// 
        /// Additional data describing physics.
        /// 
        [SerializeField]
        public SerializableMeta Meta;
        /// 
        /// TODO Document.
        /// 
        [SerializeField]
        public SerializablePhysicsSettings[] PhysicsSettings;
        #endregion
        #region Json Helpers
        /// 
        /// 2-component vector.
        /// 
        [Serializable]
        public struct SerializableVector2
        {
            [SerializeField]
            public float X;
            [SerializeField]
            public float Y;
        }
        /// 
        /// TODO Document.
        /// 
        [Serializable]
        public struct SerializableNormalizationValue
        {
            /// 
            /// Minimum of normalization.
            /// 
            [SerializeField]
            public float Minimum;
            /// 
            /// Center of normalization range.
            /// 
            [SerializeField]
            public float Default;
            /// 
            /// Maximum of normalization.
            /// 
            [SerializeField]
            public float Maximum;
        }
        /// 
        /// Target parameter of model.
        /// 
        [Serializable]
        public struct SerializableParameter
        {
            /// 
            /// Target type.
            /// 
            [SerializeField]
            public string Target;
            /// 
            /// Parameter ID.
            /// 
            [SerializeField]
            public string Id;
        }
        /// 
        /// TODO Document.
        /// 
        [Serializable]
        public struct SerializableInput
        {
            /// 
            /// Target parameter.
            /// 
            [SerializeField]
            public SerializableParameter Source;
            /// 
            /// Influence ratio of each kind.
            /// 
            [SerializeField]
            public float Weight;
            /// 
            /// Type of source.
            /// 
            [SerializeField]
            public string Type;
            /// 
            /// TODO Document.
            /// 
            [SerializeField]
            public bool Reflect;
        }
        /// 
        /// TODO Document.
        /// 
        [Serializable]
        public struct SerializableOutput
        {
            /// 
            /// Target parameter.
            /// 
            [SerializeField]
            public SerializableParameter Destination;
            /// 
            /// Index of referenced vertex.
            /// 
            [SerializeField]
            public int VertexIndex;
            /// 
            /// Scale.
            /// 
            [SerializeField]
            public float Scale;
            /// 
            /// Influence ratio of each kind.
            /// 
            [SerializeField]
            public float Weight;
            /// 
            /// Type of destination.
            /// 
            [SerializeField]
            public string Type;
            /// 
            /// TODO Document.
            /// 
            [SerializeField]
            public bool Reflect;
        }
        /// 
        /// Single vertex.
        /// 
        [Serializable]
        public struct SerializableVertex
        {
            /// 
            ///  Default position.
            /// 
            [SerializeField]
            public SerializableVector2 Position;
            /// 
            /// Mobility.
            /// 
            [SerializeField]
            public float Mobility;
            /// 
            /// Delay ratio.
            /// 
            [SerializeField]
            public float Delay;
            /// 
            /// Acceleration.
            /// 
            [SerializeField]
            public float Acceleration;
            /// 
            /// Length.
            /// 
            [SerializeField]
            public float Radius;
        }
        /// 
        /// TODO Document.
        /// 
        [Serializable]
        public struct SerializableNormalization
        {
            /// 
            /// Normalization value of position.
            /// 
            [SerializeField]
            public SerializableNormalizationValue Position;
            /// 
            /// Normalization value of angle.
            /// 
            [SerializeField]
            public SerializableNormalizationValue Angle;
        }
        /// 
        /// Setting of physics calculation.
        /// 
        [Serializable]
        public struct SerializablePhysicsSettings
        {
            /// 
            /// TODO Document.
            /// 
            [SerializeField]
            public string Id;
            /// 
            /// Input array.
            /// 
            [SerializeField]
            public SerializableInput[] Input;
            /// 
            /// Output array.
            /// 
            [SerializeField]
            public SerializableOutput[] Output;
            /// 
            /// Vertices.
            /// 
            [SerializeField]
            public SerializableVertex[] Vertices;
            /// 
            /// Normalization parameter of using input.
            /// 
            [SerializeField]
            public SerializableNormalization Normalization;
        }
        /// 
        /// Additional data describing physics.
        /// 
        [Serializable]
        public struct SerializableMeta
        {
            /// 
            /// Number of physics settings.
            /// 
            [SerializeField]
            public int PhysicsSettingCount;
            /// 
            /// Total number of input parameters.
            /// 
            [SerializeField]
            public int TotalInputCount;
            /// 
            /// Total number of output parameters.
            /// 
            [SerializeField]
            public int TotalOutputCount;
            /// 
            /// Total number of vertices.
            /// 
            [SerializeField]
            public int TotalVertexCount;
            /// 
            /// TODO Document.
            /// 
            [SerializeField]
            public SerializableEffectiveForces EffectiveForces;
        }
        /// 
        /// TODO Document.
        /// 
        [Serializable]
        public struct SerializableEffectiveForces
        {
            /// 
            /// Gravity.
            /// 
            [SerializeField]
            public SerializableVector2 Gravity;
            /// 
            /// Wind. (Not in use)
            /// 
            [SerializeField]
            public SerializableVector2 Wind;
        }
        #endregion
    }
}