using ABI.CCK.Components;
using NAK.AlternateIKSystem.VRIKHelpers;
using RootMotion.FinalIK;
using UnityEngine;

namespace NAK.AlternateIKSystem.IK.IKHandlers;

internal abstract class IKHandler
{
    #region Variables

    internal VRIK _vrik;
    internal IKSolverVR _solver;

    internal bool shouldTrackAll = true;
    internal bool shouldTrackHead = true;
    internal bool shouldTrackLeftArm = true;
    internal bool shouldTrackRightArm = true;
    internal bool shouldTrackLeftLeg = true;
    internal bool shouldTrackRightLeg = true;
    internal bool shouldTrackPelvis = true;
    internal bool shouldTrackLocomotion = true;

    // VRIK Calibration Info
    internal VRIKLocomotionData _locomotionData;

    // Last Movement Parent Info
    internal Vector3 _movementPosition;
    internal Quaternion _movementRotation;
    internal CVRMovementParent _movementParent;

    // Solver Info
    internal float _scaleDifference = 1f;
    internal float _locomotionWeight = 1f;
    internal float _ikSimulatedRootAngle;
    internal bool _wasTrackingLocomotion;

    #endregion

    #region Virtual Game Methods

    public virtual void OnInitializeIk() { }

    public virtual void OnPlayerScaled(float scaleDifference)
    {
        VRIKUtils.ApplyScaleToVRIK
        (
            _vrik,
            _locomotionData,
            _scaleDifference = scaleDifference
        );
    }

    public virtual void OnPlayerHandleMovementParent(CVRMovementParent currentParent, Vector3 platformPivot)
    {
        Vector3 currentPosition = currentParent._referencePoint.position;
        Quaternion currentRotation = Quaternion.Euler(0f, currentParent.transform.rotation.eulerAngles.y, 0f);

        Vector3 deltaPosition = currentPosition - _movementPosition;
        Quaternion deltaRotation = Quaternion.Inverse(_movementRotation) * currentRotation;

        if (_movementParent == currentParent)
        {
            _solver.AddPlatformMotion(deltaPosition, deltaRotation, platformPivot);
            _ikSimulatedRootAngle = Mathf.Repeat(_ikSimulatedRootAngle + deltaRotation.eulerAngles.y, 360f);
        }

        _movementParent = currentParent;
        _movementPosition = currentPosition;
        _movementRotation = currentRotation;
    }

    #endregion

    #region Virtual IK Weights

    public virtual void UpdateWeights()
    {
        if (!shouldTrackAll)
            return;

        if (shouldTrackHead)
            Update_HeadWeight();

        if (shouldTrackLeftArm)
            Update_LeftArmWeight();

        if (shouldTrackRightArm)
            Update_RightArmWeight();

        if (shouldTrackLeftLeg)
            Update_LeftLegWeight();

        if (shouldTrackRightLeg)
            Update_RightLegWeight();

        if (shouldTrackPelvis)
            Update_PelvisWeight();

        if (shouldTrackLocomotion)
        {
            Update_LocomotionWeight();
            ResetSolverIfNeeded();
        }

        Update_IKPositionWeight();
    }

    protected virtual void Update_HeadWeight()
    {
        float targetWeight = GetTargetWeight(BodyControl.TrackingHead, true);
    }

    protected virtual void Update_LeftArmWeight()
    {
        float leftArmWeight = GetTargetWeight(BodyControl.TrackingLeftArm, _solver.leftArm.target != null);
    }

    protected virtual void Update_RightArmWeight()
    {
        float rightArmWeight = GetTargetWeight(BodyControl.TrackingRightArm, _solver.rightArm.target != null);
    }

    protected virtual void Update_LeftLegWeight()
    {
        float leftLegWeight = GetTargetWeight(BodyControl.TrackingLeftLeg, _solver.leftLeg.target != null);
    }

    protected virtual void Update_RightLegWeight()
    {
        float rightLegWeight = GetTargetWeight(BodyControl.TrackingRightLeg, _solver.rightLeg.target != null);
    }

    protected virtual void Update_PelvisWeight()
    {
        float pelvisWeight = GetTargetWeight(BodyControl.TrackingPelvis, _solver.spine.pelvisTarget != null);
    }

    protected virtual void Update_LocomotionWeight()
    {
        _locomotionWeight = Mathf.Lerp(_locomotionWeight, BodyControl.TrackingLocomotion ? 1f : 0f,
            Time.deltaTime * ModSettings.EntryIKLerpSpeed.Value * 2f);
    }

    protected virtual void Update_IKPositionWeight()
    {
        float ikPositionWeight = BodyControl.TrackingAll ? BodyControl.TrackingIKPositionWeight : 0f;
    }

    protected virtual float GetTargetWeight(bool isTracking, bool hasTarget)
    {
        return isTracking && hasTarget ? 1f : 0f;
    }

    #endregion

    #region Private Methods

    private void ResetSolverIfNeeded()
    {
        if (_wasTrackingLocomotion == BodyControl.TrackingLocomotion)
            return;

        _wasTrackingLocomotion = BodyControl.TrackingLocomotion;
        VRIKUtils.ResetToInitialFootsteps(_vrik, _locomotionData, _scaleDifference);
        _solver.Reset();
    }

    #endregion
}