Editor/HumanoidRetargeter/Embedded/ValveResourceFormat/Resource/ResourceTypes/ModelFlex/FlexRule.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeterVrf.Utils;
using HumanoidRetargeterVrf.ResourceTypes.ModelFlex.FlexOps;

namespace HumanoidRetargeterVrf.ResourceTypes.ModelFlex
{
    /// <summary>
    /// Represents a flex rule that evaluates flex operations.
    /// </summary>
    public class FlexRule
    {
        /// <summary>
        /// Gets the flex ID.
        /// </summary>
        public int FlexID { get; }
        /// <summary>
        /// Gets the flex operations for this rule.
        /// </summary>
        public FlexOp[] FlexOps { get; }
        private readonly Stack<float> stack = new();

        /// <summary>
        /// Initializes a new instance of the <see cref="FlexRule"/> class.
        /// </summary>
        public FlexRule(int flexID, FlexOp[] flexOps)
        {
            if (flexOps.Length == 0)
            {
                throw new ArgumentException("Flex ops array cannot be empty");
            }

            FlexID = flexID;
            FlexOps = flexOps;
        }

        /// <summary>
        /// Evaluates the flex rule with the given controller values.
        /// </summary>
        public float Evaluate(float[] flexControllerValues)
        {
            var context = new FlexRuleContext(stack, flexControllerValues);

            foreach (var item in FlexOps)
            {
                item.Run(context);
            }

            if (stack.Count != 1)
            {
                throw new InvalidOperationException($"FlexRule stack had {stack.Count} values after evaluation");
            }

            return context.Stack.Pop();
        }
    }
}