using UnityEngine;

namespace pdinklag.CPW.Common
{
    /// <summary>
    /// Provides utilities and default properties for body descriptions.
    /// </summary>
    public static class BodyDescriptionUtil
    {
        /// <summary>
        /// Gets the first bone from an array of bones that is not null for the specified animator.
        /// </summary>
        /// <param name="anim">the animator</param>
        /// <param name="bones">the bones in question</param>
        public static Transform GetFirstNonNullBone(this Animator anim, InlineArray<HumanBodyBones> bones)
        {
            var bone = bones[0].For(anim);
            for (var i = 1; bone == null && i < bones.Length; i++)
            {
                bone = bones[i].For(anim);
            }
            return bone;
        }

        /// <summary>
        /// Gets the preferred bone to be the parent of a point body part.
        /// </summary>
        /// <param name="poi">the POI in question</param>
        /// <param name="anim">the animator the bone of which to retrieve</param>
        public static Transform GetPreferredParentBone(this BodyPOI poi, Animator anim)
        {
            switch (poi)
            {
                case BodyPOI.Base:
                    return HumanBodyBones.Hips.For(anim);

                case BodyPOI.HeadTop:
                case BodyPOI.Forehead:
                case BodyPOI.LeftParietal:
                case BodyPOI.RightParietal:
                case BodyPOI.ParietalBack:
                case BodyPOI.OccipitalBack:
                case BodyPOI.LeftEarBehind:
                case BodyPOI.RightEarBehind:
                case BodyPOI.LeftEarCenter:
                case BodyPOI.RightEarCenter:
                case BodyPOI.LeftEarlobe:
                case BodyPOI.RightEarlobe:
                case BodyPOI.LeftEyebrow:
                case BodyPOI.RightEyebrow:
                case BodyPOI.LeftCheekbone:
                case BodyPOI.RightCheekbone:
                case BodyPOI.LeftTemple:
                case BodyPOI.RightTemple:
                case BodyPOI.NoseBridge:
                case BodyPOI.NoseTip:
                case BodyPOI.LeftMouthCorner:
                case BodyPOI.RightMouthCorner:
                case BodyPOI.UpperLip:
                case BodyPOI.LowerLip:
                case BodyPOI.Chin:
                case BodyPOI.LeftJawAngle:
                case BodyPOI.RightJawAngle:
                    return HumanBodyBones.Head.For(anim);

                case BodyPOI.LeftEye:
                    return GetFirstNonNullBone(anim, new(HumanBodyBones.LeftEye, HumanBodyBones.Head));

                case BodyPOI.RightEye:
                    return GetFirstNonNullBone(anim, new(HumanBodyBones.RightEye, HumanBodyBones.Head));

                case BodyPOI.Throat:
                case BodyPOI.NeckBack:
                case BodyPOI.LeftNeckSide:
                case BodyPOI.RightNeckSide:
                    return GetFirstNonNullBone(anim, new(HumanBodyBones.Neck, HumanBodyBones.UpperChest, HumanBodyBones.Chest, HumanBodyBones.Spine));

                case BodyPOI.LeftTrapezius:
                case BodyPOI.RightTrapezius:
                case BodyPOI.LeftShoulderLateral:
                case BodyPOI.RightShoulderLateral:
                    return GetFirstNonNullBone(anim, new(HumanBodyBones.UpperChest, HumanBodyBones.Chest, HumanBodyBones.Spine));

                case BodyPOI.LeftShoulderFront:
                case BodyPOI.RightShoulderFront:
                case BodyPOI.LeftShoulderBack:
                case BodyPOI.RightShoulderBack:
                case BodyPOI.Sternum:
                case BodyPOI.UpperSpine:
                    return GetFirstNonNullBone(anim, new(HumanBodyBones.Chest, HumanBodyBones.Spine));

                case BodyPOI.LeftLateral:
                case BodyPOI.RightLateral:
                case BodyPOI.LeftPectorial:
                case BodyPOI.RightPectorial:
                case BodyPOI.LeftNipple:
                case BodyPOI.RightNipple:
                    return HumanBodyBones.Spine.For(anim);

                case BodyPOI.Stomach:
                case BodyPOI.Thoracic:
                case BodyPOI.LeftAbdomen:
                case BodyPOI.RightAbdomen:
                case BodyPOI.Belly:
                case BodyPOI.Lumbar:
                case BodyPOI.LeftWaist:
                case BodyPOI.RightWaist:
                case BodyPOI.Groin:
                case BodyPOI.Buttocks:
                case BodyPOI.Perineum:
                case BodyPOI.LeftPelvisLateral:
                case BodyPOI.RightPelvisLateral:
                    return HumanBodyBones.Hips.For(anim);

                case BodyPOI.LeftArmpit:
                case BodyPOI.LeftUpperArmFront:
                case BodyPOI.LeftUpperArmBack:
                case BodyPOI.LeftUpperArmMedial:
                case BodyPOI.LeftUpperArmLateral:
                case BodyPOI.LeftElbow:
                case BodyPOI.LeftInnerElbow:
                case BodyPOI.LeftElbowMedial:
                case BodyPOI.LeftElbowLateral:
                    return HumanBodyBones.LeftUpperArm.For(anim);

                case BodyPOI.RightArmpit:
                case BodyPOI.RightUpperArmFront:
                case BodyPOI.RightUpperArmBack:
                case BodyPOI.RightUpperArmMedial:
                case BodyPOI.RightUpperArmLateral:
                case BodyPOI.RightElbow:
                case BodyPOI.RightInnerElbow:
                case BodyPOI.RightElbowMedial:
                case BodyPOI.RightElbowLateral:
                    return HumanBodyBones.RightUpperArm.For(anim);

                case BodyPOI.LeftLowerArmFront:
                case BodyPOI.LeftLowerArmBack:
                case BodyPOI.LeftLowerArmMedial:
                case BodyPOI.LeftLowerArmLateral:
                case BodyPOI.LeftWristMedial:
                case BodyPOI.LeftWristLateral:
                case BodyPOI.LeftWristFront:
                case BodyPOI.LeftWristBack:
                    return HumanBodyBones.LeftLowerArm.For(anim);

                case BodyPOI.RightLowerArmFront:
                case BodyPOI.RightLowerArmBack:
                case BodyPOI.RightLowerArmMedial:
                case BodyPOI.RightLowerArmLateral:
                case BodyPOI.RightWristMedial:
                case BodyPOI.RightWristLateral:
                case BodyPOI.RightWristFront:
                case BodyPOI.RightWristBack:
                    return HumanBodyBones.RightLowerArm.For(anim);

                case BodyPOI.LeftHandPalm:
                case BodyPOI.LeftHandBack:
                case BodyPOI.LeftHandSide:
                case BodyPOI.LeftKnuckle:
                    return HumanBodyBones.LeftHand.For(anim);

                case BodyPOI.RightHandPalm:
                case BodyPOI.RightHandBack:
                case BodyPOI.RightHandSide:
                case BodyPOI.RightKnuckle:
                    return HumanBodyBones.RightHand.For(anim);

                case BodyPOI.LeftIndexTip:
                    return GetFirstNonNullBone(anim, new(
                        HumanBodyBones.LeftIndexDistal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftHand));

                case BodyPOI.RightIndexTip:
                    return GetFirstNonNullBone(anim, new(
                        HumanBodyBones.RightIndexDistal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexProximal, HumanBodyBones.RightHand));

                case BodyPOI.LeftThumbTip:
                    return GetFirstNonNullBone(anim, new(
                        HumanBodyBones.LeftThumbDistal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftHand));

                case BodyPOI.RightThumbTip:
                    return GetFirstNonNullBone(anim, new(
                        HumanBodyBones.RightThumbDistal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbProximal, HumanBodyBones.RightHand));

                case BodyPOI.LeftThighFront:
                case BodyPOI.LeftThighBack:
                case BodyPOI.LeftThighMedial:
                case BodyPOI.LeftThighLateral:
                case BodyPOI.LeftKnee:
                case BodyPOI.LeftBackKnee:
                case BodyPOI.LeftKneeMedial:
                case BodyPOI.LeftKneeLateral:
                    return HumanBodyBones.LeftUpperLeg.For(anim);

                case BodyPOI.RightThighFront:
                case BodyPOI.RightThighBack:
                case BodyPOI.RightThighMedial:
                case BodyPOI.RightThighLateral:
                case BodyPOI.RightKnee:
                case BodyPOI.RightBackKnee:
                case BodyPOI.RightKneeMedial:
                case BodyPOI.RightKneeLateral:
                    return HumanBodyBones.RightUpperLeg.For(anim);

                case BodyPOI.LeftCalfMedial:
                case BodyPOI.LeftCalfLateral:
                case BodyPOI.LeftCalf:
                case BodyPOI.LeftShin:
                case BodyPOI.LeftAchillesHeel:
                case BodyPOI.LeftAnkleFront:
                case BodyPOI.LeftAnkleMedial:
                case BodyPOI.LeftAnkleLateral:
                    return HumanBodyBones.LeftLowerLeg.For(anim);

                case BodyPOI.RightCalfMedial:
                case BodyPOI.RightCalfLateral:
                case BodyPOI.RightCalf:
                case BodyPOI.RightShin:
                case BodyPOI.RightAchillesHeel:
                case BodyPOI.RightAnkleFront:
                case BodyPOI.RightAnkleMedial:
                case BodyPOI.RightAnkleLateral:
                    return HumanBodyBones.RightLowerLeg.For(anim);

                case BodyPOI.LeftInstep:
                case BodyPOI.LeftHeelBack:
                case BodyPOI.LeftHeelBottom:
                case BodyPOI.LeftFootBottom:
                case BodyPOI.LeftFootMedial:
                case BodyPOI.LeftFootLateral:
                    return HumanBodyBones.LeftFoot.For(anim);

                case BodyPOI.LeftFootBall:
                case BodyPOI.LeftToesTop:
                case BodyPOI.LeftToesMedial:
                case BodyPOI.LeftToesLateral:
                case BodyPOI.LeftFootTip:
                    return GetFirstNonNullBone(anim, new(HumanBodyBones.LeftToes, HumanBodyBones.LeftFoot));

                case BodyPOI.RightInstep:
                case BodyPOI.RightHeelBack:
                case BodyPOI.RightHeelBottom:
                case BodyPOI.RightFootBottom:
                case BodyPOI.RightFootMedial:
                case BodyPOI.RightFootLateral:
                    return HumanBodyBones.RightFoot.For(anim);

                case BodyPOI.RightFootBall:
                case BodyPOI.RightToesTop:
                case BodyPOI.RightToesMedial:
                case BodyPOI.RightToesLateral:
                case BodyPOI.RightFootTip:
                    return GetFirstNonNullBone(anim, new(HumanBodyBones.RightToes, HumanBodyBones.RightFoot));

                default:
                    return anim.transform;
            };
        }

        public static Color GetPreferredColor(this BodyPOI poi)
        {
            switch (poi)
            {
                case BodyPOI.HeadTop:
                    return Color.red;

                case BodyPOI.Forehead:
                case BodyPOI.ParietalBack:
                case BodyPOI.LeftParietal:
                case BodyPOI.RightParietal:
                    return ColorExtensions.Orange;

                case BodyPOI.LeftEyebrow:
                case BodyPOI.RightEyebrow:
                case BodyPOI.LeftEarBehind:
                case BodyPOI.RightEarBehind:
                    return Color.yellow;

                case BodyPOI.LeftTemple:
                case BodyPOI.RightTemple:
                case BodyPOI.NoseBridge:
                case BodyPOI.LeftEye:
                case BodyPOI.RightEye:
                case BodyPOI.LeftEarCenter:
                case BodyPOI.RightEarCenter:
                    return Color.green;

                case BodyPOI.NoseTip:
                case BodyPOI.LeftCheekbone:
                case BodyPOI.RightCheekbone:
                case BodyPOI.OccipitalBack:
                    return Color.cyan;

                case BodyPOI.UpperLip:
                case BodyPOI.LeftEarlobe:
                case BodyPOI.RightEarlobe:
                    return ColorExtensions.LightBlue;

                case BodyPOI.LeftMouthCorner:
                case BodyPOI.RightMouthCorner:
                case BodyPOI.LowerLip:
                    return ColorExtensions.Violet;

                case BodyPOI.LeftJawAngle:
                case BodyPOI.RightJawAngle:
                case BodyPOI.Chin:
                    return Color.magenta;

                case BodyPOI.Throat:
                case BodyPOI.LeftNeckSide:
                case BodyPOI.RightNeckSide:
                case BodyPOI.NeckBack:
                    return Color.red;

                case BodyPOI.LeftTrapezius:
                case BodyPOI.RightTrapezius:
                    return ColorExtensions.Orange;

                case BodyPOI.LeftShoulderFront:
                case BodyPOI.LeftShoulderBack:
                case BodyPOI.LeftShoulderLateral:
                case BodyPOI.RightShoulderFront:
                case BodyPOI.RightShoulderBack:
                case BodyPOI.RightShoulderLateral:
                    return Color.yellow;

                case BodyPOI.Sternum:
                case BodyPOI.UpperSpine:
                case BodyPOI.LeftArmpit:
                case BodyPOI.LeftPectorial:
                case BodyPOI.RightArmpit:
                case BodyPOI.RightPectorial:
                    return Color.green;

                case BodyPOI.LeftNipple:
                case BodyPOI.LeftLateral:
                case BodyPOI.RightNipple:
                case BodyPOI.RightLateral:
                    return Color.cyan;

                case BodyPOI.Stomach:
                case BodyPOI.Thoracic:
                case BodyPOI.LeftAbdomen:
                case BodyPOI.RightAbdomen:
                    return ColorExtensions.LightBlue;

                case BodyPOI.Belly:
                case BodyPOI.Lumbar:
                case BodyPOI.LeftWaist:
                case BodyPOI.RightWaist:
                    return ColorExtensions.Violet;

                case BodyPOI.Groin:
                case BodyPOI.Buttocks:
                case BodyPOI.Perineum:
                case BodyPOI.LeftPelvisLateral:
                case BodyPOI.RightPelvisLateral:
                    return Color.magenta;

                case BodyPOI.LeftUpperArmBack:
                case BodyPOI.LeftUpperArmFront:
                case BodyPOI.LeftUpperArmMedial:
                case BodyPOI.LeftUpperArmLateral:
                case BodyPOI.RightUpperArmBack:
                case BodyPOI.RightUpperArmFront:
                case BodyPOI.RightUpperArmMedial:
                case BodyPOI.RightUpperArmLateral:
                    return Color.green;

                case BodyPOI.LeftElbow:
                case BodyPOI.LeftInnerElbow:
                case BodyPOI.LeftElbowMedial:
                case BodyPOI.LeftElbowLateral:
                case BodyPOI.RightElbow:
                case BodyPOI.RightInnerElbow:
                case BodyPOI.RightElbowMedial:
                case BodyPOI.RightElbowLateral:
                    return Color.cyan;

                case BodyPOI.LeftLowerArmBack:
                case BodyPOI.LeftLowerArmFront:
                case BodyPOI.LeftLowerArmMedial:
                case BodyPOI.LeftLowerArmLateral:
                case BodyPOI.RightLowerArmBack:
                case BodyPOI.RightLowerArmFront:
                case BodyPOI.RightLowerArmMedial:
                case BodyPOI.RightLowerArmLateral:
                    return ColorExtensions.LightBlue;

                case BodyPOI.LeftWristBack:
                case BodyPOI.LeftWristFront:
                case BodyPOI.LeftWristMedial:
                case BodyPOI.LeftWristLateral:
                case BodyPOI.RightWristBack:
                case BodyPOI.RightWristFront:
                case BodyPOI.RightWristMedial:
                case BodyPOI.RightWristLateral:
                    return ColorExtensions.Violet;

                case BodyPOI.LeftHandBack:
                case BodyPOI.LeftHandPalm:
                case BodyPOI.LeftHandSide:
                case BodyPOI.RightHandBack:
                case BodyPOI.RightHandPalm:
                case BodyPOI.RightHandSide:
                    return Color.magenta;

                case BodyPOI.LeftKnuckle:
                case BodyPOI.LeftIndexTip:
                case BodyPOI.LeftThumbTip:
                case BodyPOI.RightKnuckle:
                case BodyPOI.RightIndexTip:
                case BodyPOI.RightThumbTip:
                    return Color.red;

                case BodyPOI.LeftThighBack:
                case BodyPOI.LeftThighFront:
                case BodyPOI.LeftThighLateral:
                case BodyPOI.LeftThighMedial:
                case BodyPOI.RightThighBack:
                case BodyPOI.RightThighFront:
                case BodyPOI.RightThighLateral:
                case BodyPOI.RightThighMedial:
                    return ColorExtensions.Orange;

                case BodyPOI.LeftKnee:
                case BodyPOI.LeftBackKnee:
                case BodyPOI.LeftKneeLateral:
                case BodyPOI.LeftKneeMedial:
                case BodyPOI.RightKnee:
                case BodyPOI.RightBackKnee:
                case BodyPOI.RightKneeLateral:
                case BodyPOI.RightKneeMedial:
                    return Color.yellow;

                case BodyPOI.LeftShin:
                case BodyPOI.LeftCalf:
                case BodyPOI.LeftCalfLateral:
                case BodyPOI.LeftCalfMedial:
                case BodyPOI.RightShin:
                case BodyPOI.RightCalf:
                case BodyPOI.RightCalfLateral:
                case BodyPOI.RightCalfMedial:
                    return Color.green;

                case BodyPOI.LeftAchillesHeel:
                case BodyPOI.LeftAnkleFront:
                case BodyPOI.LeftAnkleLateral:
                case BodyPOI.LeftAnkleMedial:
                case BodyPOI.RightAchillesHeel:
                case BodyPOI.RightAnkleFront:
                case BodyPOI.RightAnkleLateral:
                case BodyPOI.RightAnkleMedial:
                    return Color.cyan;

                case BodyPOI.LeftHeelBack:
                case BodyPOI.LeftHeelBottom:
                case BodyPOI.RightHeelBack:
                case BodyPOI.RightHeelBottom:
                    return ColorExtensions.LightBlue;

                case BodyPOI.LeftInstep:
                case BodyPOI.LeftFootBottom:
                case BodyPOI.LeftFootLateral:
                case BodyPOI.LeftFootMedial:
                case BodyPOI.RightInstep:
                case BodyPOI.RightFootBottom:
                case BodyPOI.RightFootLateral:
                case BodyPOI.RightFootMedial:
                    return ColorExtensions.Violet;

                case BodyPOI.LeftFootBall:
                case BodyPOI.LeftToesTop:
                case BodyPOI.LeftToesMedial:
                case BodyPOI.LeftToesLateral:
                case BodyPOI.RightFootBall:
                case BodyPOI.RightToesTop:
                case BodyPOI.RightToesMedial:
                case BodyPOI.RightToesLateral:
                    return Color.magenta;

                case BodyPOI.LeftFootTip:
                case BodyPOI.RightFootTip:
                    return Color.red;

                default:
                    if (poi != BodyPOI.Base) Debug.LogWarningFormat("no default color defined for BodyPOI.{0}", poi);
                    return Color.gray;
            }
        }
    }
}
