Editor/HumanoidMocap/MocapAdjustments.cs
using System;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using HumanoidMocap.Cleanup;
using HumanoidMocap.Motion;

namespace HumanoidMocap.Editor;

public sealed partial class RetargetWindow
{
    MocapAdjustmentStore.Session _editSession;
    CleanupSettings _appliedCleanup;
    string _adjustmentSaveError;

    void ResetAdjustmentFields()
    {
        var cleanup=_appliedCleanup??new CleanupSettings();
        _rootSmooth.Text=cleanup.Root.ToString(CultureInfo.InvariantCulture);
        _armSmooth.Text=cleanup.Arms.ToString(CultureInfo.InvariantCulture);
        _fingerSmooth.Text=cleanup.Fingers.ToString(CultureInfo.InvariantCulture);
        _smoothing.Text=cleanup.Smoothing.ToString(CultureInfo.InvariantCulture);
        FitMocapPlacementToTarget();
    }

    async Task ReviewContactAsync(ContactInterval contact,ContactReview review)
    {
        if(_processing is not null)return;
        if(review==ContactReview.Confirmed&&new PropContactMotion(_editedMotion).UnsupportedReason(contact) is {} reason)
        {_captureStatus.Text=reason;return;}
        contact.Review=review;
        // Rebuild from observations so contact protection is applied before filtering.
        var source=_rawMotion.Copy();source.Contacts=_editedMotion.Copy().Contacts;
        _editedMotion=_appliedCleanup is null?source:MotionCleanup.Apply(source,_appliedCleanup);
        RefreshContacts();
        await RefreshMocapPreviewAsync();
    }

    void RestoreTargetAdjustments()
    {
        if(_editSession is null||_target is null)return;
        if(!_editSession.State.Targets.TryGetValue(MocapAdjustmentStore.TargetKey(_target.Spec,_firstPerson),out var edit))return;
        string V(System.Numerics.Vector3 v)=>FormattableString.Invariant($"{v.X:R},{v.Y:R},{v.Z:R}");
        string F(float v)=>v.ToString("R",CultureInfo.InvariantCulture);
        var c=edit.Corrections;
        _shoulderL.Text=V(c.LeftShoulder);_shoulderR.Text=V(c.RightShoulder);
        _elbowL.Text=V(c.LeftElbow);_elbowR.Text=V(c.RightElbow);
        _capturePosition.Text=V(c.CaptureCameraPosition);_captureYaw.Text=F(c.CaptureCameraYawDegrees);_capturePitch.Text=F(c.CaptureCameraPitchDegrees);
        _captureFacesSubject=c.CaptureFacesSubject;
        _reach.Text=F(c.Reach);_ground.Text=F(c.GroundOffset);_facing.Text=F(c.FacingDegrees);
        _stabilizeFeetControl.Value=c.StabilizeFeet;
        _wristOffsets.Clear();_wristOffsets.AddRange(c.WristOffsets.Select(e=>e.Copy()));++_wristEditRevision;
        _rootMotion=edit.RootMotion;_inPlaceControl.Value=_rootMotion==RootMotionMode.InPlace;
        _fov.Text=F(edit.Fov);_viewPitch.Text=F(edit.ViewPitch);_viewNear.Text=F(edit.NearClip);
    }

    void SaveAppliedAdjustments(MocapAdjustmentStore.Session session,string key,MocapAdjustmentStore.TargetEdit edit,
        CleanupSettings cleanup,MotionDocument motion)
    {
        if(session is null||session!=_editSession)return;
        session.State.Cleanup=cleanup;session.State.Contacts=motion.Contacts;session.State.Targets[key]=edit;
        try{MocapAdjustmentStore.Save(session);_adjustmentSaveError=null;}
        catch(Exception error){_captureStatus.Text=_adjustmentSaveError="Preview ready, but adjustments could not be saved: "+error.Message;}
    }
}