using ABI_RC.Core.InteractionSystem;
using ABI.CCK.Components;
using ABI_RC.Systems.IK.SubSystems;
using NAK.DesktopVRIK.IK.VRIKHelpers;
using RootMotion.FinalIK;
using UnityEngine;

namespace NAK.DesktopVRIK.IK.IKHandlers;

internal abstract class IKHandler
{
    #region Variables

    internal VRIK _vrik;
    internal IKSolverVR _solver;

    // 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 _ikWeight = 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);
        
        if (_movementParent != null && _movementParent == currentParent)
        {
            Vector3 deltaPosition = currentPosition - _movementPosition;
            Quaternion deltaRotation = Quaternion.identity;
        
            if (currentParent.orientationMode == CVRMovementParent.OrientationMode.RotateWithParent)
                deltaRotation = Quaternion.Inverse(_movementRotation) * currentRotation;
            
            _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()
    {
        Update_HeadWeight();

        Update_LeftArmWeight();
        Update_RightArmWeight();

        Update_LeftLegWeight();
        Update_RightLegWeight();

        Update_PelvisWeight();

        Update_LocomotionWeight();

        Update_IKPositionWeight();
    }

    public virtual void UpdateTracking() { }

    protected virtual void Update_HeadWeight()
    {
        // There is no Head tracking setting
        _solver.spine.rotationWeight = _solver.spine.positionWeight =
            GetTargetWeight(BodySystem.TrackingEnabled, _solver.spine.headTarget != null);
    }

    protected virtual void Update_LeftArmWeight()
    {
        _solver.leftArm.rotationWeight = _solver.leftArm.positionWeight =
            GetTargetWeight(BodySystem.TrackingLeftArmEnabled, _solver.leftArm.target != null);
    }

    protected virtual void Update_RightArmWeight()
    {
        _solver.rightArm.rotationWeight = _solver.rightArm.positionWeight =
            GetTargetWeight(BodySystem.TrackingRightArmEnabled, _solver.rightArm.target != null);
    }

    protected virtual void Update_LeftLegWeight()
    {
        _solver.leftLeg.rotationWeight = _solver.leftLeg.positionWeight =
            GetTargetWeight(BodySystem.TrackingLeftLegEnabled, _solver.leftLeg.target != null);
    }

    protected virtual void Update_RightLegWeight()
    {
        _solver.rightLeg.rotationWeight = _solver.rightLeg.positionWeight =
            GetTargetWeight(BodySystem.TrackingRightLegEnabled, _solver.rightLeg.target != null);
    }

    protected virtual void Update_PelvisWeight()
    {
        // There is no Pelvis tracking setting
        _solver.spine.pelvisRotationWeight = _solver.spine.pelvisPositionWeight =
            GetTargetWeight(BodySystem.TrackingEnabled, _solver.spine.pelvisTarget != null);
    }

    protected virtual void Update_LocomotionWeight()
    {
        _solver.locomotion.weight = _locomotionWeight = Mathf.Lerp(_locomotionWeight, BodySystem.TrackingLocomotionEnabled ? 1f : 0f,
            Time.deltaTime * ModSettings.EntryIKLerpSpeed.Value * 2f);
    }

    protected virtual void Update_IKPositionWeight()
    {
        _solver.IKPositionWeight = _ikWeight = Mathf.Lerp(_ikWeight, BodySystem.TrackingEnabled ? BodySystem.TrackingPositionWeight : 0f,
            Time.deltaTime * ModSettings.EntryIKLerpSpeed.Value);
    }
    
    public virtual void Reset()
    {
        _ikSimulatedRootAngle = _vrik.transform.eulerAngles.y;
        
        _solver.Reset();
        
        if(ModSettings.EntryResetFootstepsOnIdle.Value)
            VRIKUtils.ResetToInitialFootsteps(_vrik, _locomotionData, _scaleDifference);
    }
    
    #endregion

    #region Private Methods

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

    #endregion
}