using UnityEngine;

namespace pdinklag.CPW.Common
{
    /// <summary>
    /// Describes a wrestler's body.
    ///
    /// This is used for ragdoll generation and IK calculations.
    /// </summary>
    [SelectionBase]
    [RequireComponent(typeof(Animator))]
    public class BodyDescription : MonoBehaviour
    {
        /// <summary>
        /// Contains body points, one for each BodyPOI.
        /// Since the order of entries in the Points array is important and should never be changed manually, we hide this array in the inspector.
        /// </summary>
        [HideInInspector]
        public BodyPartPoint[] Points;

        public bool HasPoints => Points != null && Points.Length > 0;

        [Header("Ragdoll")]
        public BodyPartBox Head;
        public BodyPartBox UpperTorso;
        public BodyPartBox LowerTorso;

        public BodyPartCylinder LeftUpperArm;
        public BodyPartCylinder RightUpperArm;
        public BodyPartCylinder LeftLowerArm;
        public BodyPartCylinder RightLowerArm;
        public BodyPartBox LeftHand;
        public BodyPartBox RightHand;

        public BodyPartCylinder LeftUpperLeg;
        public BodyPartCylinder RightUpperLeg;
        public BodyPartCylinder LeftLowerLeg;
        public BodyPartCylinder RightLowerLeg;
        public BodyPartBox LeftFoot;
        public BodyPartBox RightFoot;

        [Header("Bone Orientations (T-Pose)")]
        public BoneOrientation HeadOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation SpineOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation LeftUpperArmOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation LeftLowerArmOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation LeftHandOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation RightUpperArmOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation RightLowerArmOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation RightHandOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation LeftUpperLegOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation LeftLowerLegOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation LeftFootOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation RightUpperLegOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation RightLowerLegOrientation = new BoneOrientation(Axis.Forward, Axis.Up);
        public BoneOrientation RightFootOrientation = new BoneOrientation(Axis.Forward, Axis.Up);

        // point getters by name
        public BodyPartPoint HeadTop => Points[(int)BodyPOI.HeadTop];

        public BodyPartPoint Forehead => Points[(int)BodyPOI.Forehead];
        public BodyPartPoint LeftParietal => Points[(int)BodyPOI.LeftParietal];
        public BodyPartPoint RightParietal => Points[(int)BodyPOI.RightParietal];
        public BodyPartPoint ParietalBack => Points[(int)BodyPOI.ParietalBack];

        public BodyPartPoint OccipitalBack => Points[(int)BodyPOI.OccipitalBack];

        public BodyPartPoint LeftEarBehind => Points[(int)BodyPOI.LeftEarBehind];
        public BodyPartPoint RightEarBehind => Points[(int)BodyPOI.RightEarBehind];
        public BodyPartPoint LeftEarCenter => Points[(int)BodyPOI.LeftEarCenter];
        public BodyPartPoint RightEarCenter => Points[(int)BodyPOI.RightEarCenter];
        public BodyPartPoint LeftEarlobe => Points[(int)BodyPOI.LeftEarlobe];
        public BodyPartPoint RightEarlobe => Points[(int)BodyPOI.RightEarlobe];
        public BodyPartPoint LeftEye => Points[(int)BodyPOI.LeftEye];
        public BodyPartPoint RightEye => Points[(int)BodyPOI.RightEye];
        public BodyPartPoint LeftEyebrow => Points[(int)BodyPOI.LeftEyebrow];
        public BodyPartPoint RightEyebrow => Points[(int)BodyPOI.RightEyebrow];
        public BodyPartPoint LeftCheekbone => Points[(int)BodyPOI.LeftCheekbone];
        public BodyPartPoint RightCheekbone => Points[(int)BodyPOI.RightCheekbone];
        public BodyPartPoint LeftTemple => Points[(int)BodyPOI.LeftTemple];
        public BodyPartPoint RightTemple => Points[(int)BodyPOI.RightTemple];
        public BodyPartPoint NoseBridge => Points[(int)BodyPOI.NoseBridge];
        public BodyPartPoint NoseTip => Points[(int)BodyPOI.NoseTip];
        public BodyPartPoint LeftMouthCorner => Points[(int)BodyPOI.LeftMouthCorner];
        public BodyPartPoint RightMouthCorner => Points[(int)BodyPOI.RightMouthCorner];
        public BodyPartPoint UpperLip => Points[(int)BodyPOI.UpperLip];
        public BodyPartPoint LowerLip => Points[(int)BodyPOI.LowerLip];
        public BodyPartPoint Chin => Points[(int)BodyPOI.Chin];
        public BodyPartPoint LeftJawAngle => Points[(int)BodyPOI.LeftJawAngle];
        public BodyPartPoint RightJawAngle => Points[(int)BodyPOI.RightJawAngle];

        public BodyPartPoint Throat => Points[(int)BodyPOI.Throat];
        public BodyPartPoint NeckBack => Points[(int)BodyPOI.NeckBack];
        public BodyPartPoint LeftNeckSide => Points[(int)BodyPOI.LeftNeckSide];
        public BodyPartPoint RightNeckSide => Points[(int)BodyPOI.RightNeckSide];

        public BodyPartPoint LeftTrapezius => Points[(int)BodyPOI.LeftTrapezius];
        public BodyPartPoint RightTrapezius => Points[(int)BodyPOI.RightTrapezius];

        public BodyPartPoint LeftShoulderFront => Points[(int)BodyPOI.LeftShoulderFront];
        public BodyPartPoint RightShoulderFront => Points[(int)BodyPOI.RightShoulderFront];
        public BodyPartPoint LeftShoulderBack => Points[(int)BodyPOI.LeftShoulderBack];
        public BodyPartPoint RightShoulderBack => Points[(int)BodyPOI.RightShoulderBack];
        public BodyPartPoint LeftShoulderLateral => Points[(int)BodyPOI.LeftShoulderLateral];
        public BodyPartPoint RightShoulderLateral => Points[(int)BodyPOI.RightShoulderLateral];

        public BodyPartPoint Sternum => Points[(int)BodyPOI.Sternum];
        public BodyPartPoint UpperSpine => Points[(int)BodyPOI.UpperSpine];
        public BodyPartPoint LeftLateral => Points[(int)BodyPOI.LeftLateral];
        public BodyPartPoint RightLateral => Points[(int)BodyPOI.RightLateral];

        public BodyPartPoint LeftPectorial => Points[(int)BodyPOI.LeftPectorial];
        public BodyPartPoint RightPectorial => Points[(int)BodyPOI.RightPectorial];

        public BodyPartPoint LeftNipple => Points[(int)BodyPOI.LeftNipple];
        public BodyPartPoint RightNipple => Points[(int)BodyPOI.RightNipple];

        public BodyPartPoint Stomach => Points[(int)BodyPOI.Stomach];
        public BodyPartPoint Thoracic => Points[(int)BodyPOI.Thoracic];
        public BodyPartPoint LeftAbdomen => Points[(int)BodyPOI.LeftAbdomen];
        public BodyPartPoint RightAbdomen => Points[(int)BodyPOI.RightAbdomen];

        public BodyPartPoint Belly => Points[(int)BodyPOI.Belly];
        public BodyPartPoint Lumbar => Points[(int)BodyPOI.Lumbar];
        public BodyPartPoint LeftWaist => Points[(int)BodyPOI.LeftWaist];
        public BodyPartPoint RightWaist => Points[(int)BodyPOI.RightWaist];

        public BodyPartPoint Groin => Points[(int)BodyPOI.Groin];
        public BodyPartPoint Buttocks => Points[(int)BodyPOI.Buttocks];
        public BodyPartPoint Perineum => Points[(int)BodyPOI.Perineum];
        public BodyPartPoint LeftPelvisLateral => Points[(int)BodyPOI.LeftPelvisLateral];
        public BodyPartPoint RightPelvisLateral => Points[(int)BodyPOI.RightPelvisLateral];

        public BodyPartPoint LeftArmpit => Points[(int)BodyPOI.LeftArmpit];
        public BodyPartPoint RightArmpit => Points[(int)BodyPOI.RightArmpit];

        public BodyPartPoint LeftUpperArmFront => Points[(int)BodyPOI.LeftUpperArmFront];
        public BodyPartPoint LeftUpperArmBack => Points[(int)BodyPOI.LeftUpperArmBack];
        public BodyPartPoint LeftUpperArmMedial => Points[(int)BodyPOI.LeftUpperArmMedial];
        public BodyPartPoint LeftUpperArmLateral => Points[(int)BodyPOI.LeftUpperArmLateral];

        public BodyPartPoint RightUpperArmFront => Points[(int)BodyPOI.RightUpperArmFront];
        public BodyPartPoint RightUpperArmBack => Points[(int)BodyPOI.RightUpperArmBack];
        public BodyPartPoint RightUpperArmMedial => Points[(int)BodyPOI.RightUpperArmMedial];
        public BodyPartPoint RightUpperArmLateral => Points[(int)BodyPOI.RightUpperArmLateral];

        public BodyPartPoint LeftElbow => Points[(int)BodyPOI.LeftElbow];
        public BodyPartPoint LeftInnerElbow => Points[(int)BodyPOI.LeftInnerElbow];
        public BodyPartPoint LeftElbowMedial => Points[(int)BodyPOI.LeftElbowMedial];
        public BodyPartPoint LeftElbowLateral => Points[(int)BodyPOI.LeftElbowLateral];

        public BodyPartPoint RightElbow => Points[(int)BodyPOI.RightElbow];
        public BodyPartPoint RightInnerElbow => Points[(int)BodyPOI.RightInnerElbow];
        public BodyPartPoint RightElbowMedial => Points[(int)BodyPOI.RightElbowMedial];
        public BodyPartPoint RightElbowLateral => Points[(int)BodyPOI.RightElbowLateral];

        public BodyPartPoint LeftLowerArmFront => Points[(int)BodyPOI.LeftLowerArmFront];
        public BodyPartPoint LeftLowerArmBack => Points[(int)BodyPOI.LeftLowerArmBack];
        public BodyPartPoint LeftLowerArmMedial => Points[(int)BodyPOI.LeftLowerArmMedial];
        public BodyPartPoint LeftLowerArmLateral => Points[(int)BodyPOI.LeftLowerArmLateral];
        public BodyPartPoint RightLowerArmFront => Points[(int)BodyPOI.RightLowerArmFront];
        public BodyPartPoint RightLowerArmBack => Points[(int)BodyPOI.RightLowerArmBack];
        public BodyPartPoint RightLowerArmMedial => Points[(int)BodyPOI.RightLowerArmMedial];
        public BodyPartPoint RightLowerArmLateral => Points[(int)BodyPOI.RightLowerArmLateral];

        public BodyPartPoint LeftWristMedial => Points[(int)BodyPOI.LeftWristMedial];
        public BodyPartPoint LeftWristLateral => Points[(int)BodyPOI.LeftWristLateral];
        public BodyPartPoint LeftWristFront => Points[(int)BodyPOI.LeftWristFront];
        public BodyPartPoint LeftWristBack => Points[(int)BodyPOI.LeftWristBack];
        public BodyPartPoint RightWristMedial => Points[(int)BodyPOI.RightWristMedial];
        public BodyPartPoint RightWristLateral => Points[(int)BodyPOI.RightWristLateral];
        public BodyPartPoint RightWristFront => Points[(int)BodyPOI.RightWristFront];
        public BodyPartPoint RightWristBack => Points[(int)BodyPOI.RightWristBack];

        public BodyPartPoint LeftHandPalm => Points[(int)BodyPOI.LeftHandPalm];
        public BodyPartPoint RightHandPalm => Points[(int)BodyPOI.RightHandPalm];
        public BodyPartPoint LeftHandBack => Points[(int)BodyPOI.LeftHandBack];
        public BodyPartPoint RightHandBack => Points[(int)BodyPOI.RightHandBack];
        public BodyPartPoint LeftHandSide => Points[(int)BodyPOI.LeftHandSide];
        public BodyPartPoint RightHandSide => Points[(int)BodyPOI.RightHandSide];
        public BodyPartPoint LeftKnuckle => Points[(int)BodyPOI.LeftKnuckle];
        public BodyPartPoint RightKnuckle => Points[(int)BodyPOI.RightKnuckle];
        public BodyPartPoint LeftIndexTip => Points[(int)BodyPOI.LeftIndexTip];
        public BodyPartPoint RightIndexTip => Points[(int)BodyPOI.RightIndexTip];
        public BodyPartPoint LeftThumbTip => Points[(int)BodyPOI.LeftThumbTip];
        public BodyPartPoint RightThumbTip => Points[(int)BodyPOI.RightThumbTip];

        public BodyPartPoint LeftThighFront => Points[(int)BodyPOI.LeftThighFront];
        public BodyPartPoint LeftThighBack => Points[(int)BodyPOI.LeftThighBack];
        public BodyPartPoint LeftThighMedial => Points[(int)BodyPOI.LeftThighMedial];
        public BodyPartPoint LeftThighLateral => Points[(int)BodyPOI.LeftThighLateral];
        public BodyPartPoint RightThighFront => Points[(int)BodyPOI.RightThighFront];
        public BodyPartPoint RightThighBack => Points[(int)BodyPOI.RightThighBack];
        public BodyPartPoint RightThighMedial => Points[(int)BodyPOI.RightThighMedial];
        public BodyPartPoint RightThighLateral => Points[(int)BodyPOI.RightThighLateral];

        public BodyPartPoint LeftKnee => Points[(int)BodyPOI.LeftKnee];
        public BodyPartPoint LeftBackKnee => Points[(int)BodyPOI.LeftBackKnee];
        public BodyPartPoint LeftKneeMedial => Points[(int)BodyPOI.LeftKneeMedial];
        public BodyPartPoint LeftKneeLateral => Points[(int)BodyPOI.LeftKneeLateral];
        public BodyPartPoint RightKnee => Points[(int)BodyPOI.RightKnee];
        public BodyPartPoint RightBackKnee => Points[(int)BodyPOI.RightBackKnee];
        public BodyPartPoint RightKneeMedial => Points[(int)BodyPOI.RightKneeMedial];
        public BodyPartPoint RightKneeLateral => Points[(int)BodyPOI.RightKneeLateral];

        public BodyPartPoint LeftCalfMedial => Points[(int)BodyPOI.LeftCalfMedial];
        public BodyPartPoint LeftCalfLateral => Points[(int)BodyPOI.LeftCalfLateral];
        public BodyPartPoint RightCalfMedial => Points[(int)BodyPOI.RightCalfMedial];
        public BodyPartPoint RightCalfLateral => Points[(int)BodyPOI.RightCalfLateral];
        public BodyPartPoint LeftCalf => Points[(int)BodyPOI.LeftCalf];
        public BodyPartPoint RightCalf => Points[(int)BodyPOI.RightCalf];
        public BodyPartPoint LeftShin => Points[(int)BodyPOI.LeftShin];
        public BodyPartPoint RightShin => Points[(int)BodyPOI.RightShin];

        public BodyPartPoint LeftAchillesHeel => Points[(int)BodyPOI.LeftAchillesHeel];
        public BodyPartPoint LeftAnkleFront => Points[(int)BodyPOI.LeftAnkleFront];
        public BodyPartPoint LeftAnkleMedial => Points[(int)BodyPOI.LeftAnkleMedial];
        public BodyPartPoint LeftAnkleLateral => Points[(int)BodyPOI.LeftAnkleLateral];
        public BodyPartPoint RightAchillesHeel => Points[(int)BodyPOI.RightAchillesHeel];
        public BodyPartPoint RightAnkleFront => Points[(int)BodyPOI.RightAnkleFront];
        public BodyPartPoint RightAnkleMedial => Points[(int)BodyPOI.RightAnkleMedial];
        public BodyPartPoint RightAnkleLateral => Points[(int)BodyPOI.RightAnkleLateral];

        public BodyPartPoint LeftInstep => Points[(int)BodyPOI.LeftInstep];
        public BodyPartPoint LeftHeelBack => Points[(int)BodyPOI.LeftHeelBack];
        public BodyPartPoint LeftHeelBottom => Points[(int)BodyPOI.LeftHeelBottom];
        public BodyPartPoint LeftFootBottom => Points[(int)BodyPOI.LeftFootBottom];
        public BodyPartPoint LeftFootMedial => Points[(int)BodyPOI.LeftFootMedial];
        public BodyPartPoint LeftFootLateral => Points[(int)BodyPOI.LeftFootLateral];
        public BodyPartPoint LeftFootBall => Points[(int)BodyPOI.LeftFootBall];
        public BodyPartPoint LeftToesTop => Points[(int)BodyPOI.LeftToesTop];
        public BodyPartPoint LeftToesMedial => Points[(int)BodyPOI.LeftToesMedial];
        public BodyPartPoint LeftToesLateral => Points[(int)BodyPOI.LeftToesLateral];
        public BodyPartPoint LeftFootTip => Points[(int)BodyPOI.LeftFootTip];

        public BodyPartPoint RightInstep => Points[(int)BodyPOI.RightInstep];
        public BodyPartPoint RightHeelBack => Points[(int)BodyPOI.RightHeelBack];
        public BodyPartPoint RightHeelBottom => Points[(int)BodyPOI.RightHeelBottom];
        public BodyPartPoint RightFootBottom => Points[(int)BodyPOI.RightFootBottom];
        public BodyPartPoint RightFootMedial => Points[(int)BodyPOI.RightFootMedial];
        public BodyPartPoint RightFootLateral => Points[(int)BodyPOI.RightFootLateral];
        public BodyPartPoint RightFootBall => Points[(int)BodyPOI.RightFootBall];
        public BodyPartPoint RightToesTop => Points[(int)BodyPOI.RightToesTop];
        public BodyPartPoint RightToesMedial => Points[(int)BodyPOI.RightToesMedial];
        public BodyPartPoint RightToesLateral => Points[(int)BodyPOI.RightToesLateral];
        public BodyPartPoint RightFootTip => Points[(int)BodyPOI.RightFootTip];

        public Vector3 GetPOI(BodyPOI poi) => (poi == BodyPOI.Base) ? transform.position : Points[(int)poi].Position;
        public Transform GetPOITransform(BodyPOI poi) => Points[(int)poi].transform;

        public float Reach { get; private set; }
        public float Height { get; private set; }

        private void Awake()
        {
            Reach = RightUpperArm.Height + RightLowerArm.Height + Vector3.Distance(RightHand.Position, GetPOI(BodyPOI.RightIndexTip));
            Height = HeadTop.Position.y;
        }
    }
}
