private static void WriteCreateActionSpaceHelpers(StringBuilder sb, ActionSet actionSet, Action action, int indent, INamingConventionConverter conv)
        {
            Action <string> appendLineTrim = (string text) => sb.AppendLine(text.TrimEnd());
            Action <string> appendLine     = (string text) => appendLineTrim($"{new string(' ', indent * 4)}{text}");
            Action <bool>   addLineIfTrue  = (bool condition) => { if (condition)
                                                                   {
                                                                       appendLineTrim("");
                                                                   }
            };

            Action <TopLevelPath> writeActionSpaceFunction = (TopLevelPath subactionPath) =>
            {
                appendLine("");
                appendLine($"XrResult {conv.Rename("Create", GetNameWithSubaction(action.Name, subactionPath), "ActionSpace")}(XrSession session, XrSpace* space) const");
                appendLine("{");
                indent++;

                appendLine($"XrActionSpaceCreateInfo actionSpaceInfo{{XR_TYPE_ACTION_SPACE_CREATE_INFO}};");
                appendLine($"actionSpaceInfo.action = {GetActionHandleMemberName(action.Name, conv)};");
                appendLine($"actionSpaceInfo.poseInActionSpace.orientation.w = 1.0f;");

                if (subactionPath != TopLevelPath.Null)
                {
                    appendLine($"actionSpaceInfo.subactionPath = {GetSubactionMemberName(subactionPath, conv)};");
                }

                appendLine($"return xrCreateActionSpace(session, &actionSpaceInfo, space);");

                indent--;
                appendLine("}");
            };

            foreach (var subaction in GetActionSubactionPaths(actionSet, action, includeNull: true))
            {
                writeActionSpaceFunction(subaction);
            }
        }
 private static string GetActionHandleMemberName(string actionName, INamingConventionConverter conv) => conv.Rename(actionName);
 private static string GetActionSetStructName(ActionSet actionSet, INamingConventionConverter conv) => conv.Rename(actionSet.Name, "ActionSet");
 private static string GetActionStateMemberName(Action action, TopLevelPath subactionPath, INamingConventionConverter conv) => conv.Rename(GetNameWithSubaction(action.Name, subactionPath), "ActionState");
 public static string GetBindingVariableName(string bindingPath, INamingConventionConverter conv) => conv.Rename(bindingPath.Replace('/', '_'));
 public static string GetInteractionProfileMemberName(string interactionProfilePath, INamingConventionConverter conv) => conv.Rename(interactionProfilePath.Replace("/interaction_profiles/", "").Replace('/', '_'));
 public static string GetSuggestedBindingsMemberName(string interactionProfilePath, INamingConventionConverter conv) => conv.Rename(interactionProfilePath.Replace("/interaction_profiles/", "").Replace('/', '_'), "Bindings");
        private static void WriteActionSetStateStruct(StringBuilder sb, ActionSet actionSet, int indent, INamingConventionConverter conv)
        {
            Action <string> appendLineTrim = (string text) => sb.AppendLine(text.TrimEnd());
            Action <string> appendLine     = (string text) => appendLineTrim($"{new string(' ', indent * 4)}{text}");
            Action <bool>   addLineIfTrue  = (bool condition) => { if (condition)
                                                                   {
                                                                       appendLineTrim("");
                                                                   }
            };

            var inputActions = actionSet.Actions.Where(a => a.Type != ActionType.Haptic);

            var usedTopLevelPathsWithoutNull = inputActions.SelectMany(a => GetActionSubactionPaths(actionSet, a)).Distinct().OrderBy(s => s);

            appendLine("");
            appendLine($"struct {conv.Rename("", actionSet.Name, "ActionStates")}");
            appendLine("{");
            indent++;

            // Write out a struct with all actions that have subactions enabled for per-subaction querying.
            bool hasSubactionPath = false;

            foreach (var action in inputActions.Where(a => a.UseSubactionPaths))
            {
                if (!hasSubactionPath)
                {
                    // Delay writing struct to here so it isn't written at all if there are no subaction paths.
                    appendLine("struct SubactionStates");
                    appendLine("{");
                    indent++;
                    hasSubactionPath = true;
                    appendLine("XrPath SubactionPath = XR_NULL_PATH;");
                }

                appendLine($"const {GetActionStateStructName(action.Type)}* {GetActionStateMemberName(action, TopLevelPath.Null, conv)} = nullptr;");
            }
            if (hasSubactionPath)
            {
                indent--;
                appendLine("};");
                appendLine("");
            }

            appendLine($"XrResult UpdateActionStates(XrSession session, {GetActionSetStructName(actionSet, conv)} const& actionSet)");
            appendLine("{");
            indent++;
            appendLine("XrActionStateGetInfo actionStateGetInfo{XR_TYPE_ACTION_STATE_GET_INFO};");
            appendLine("XrResult result = XR_SUCCESS;");
            foreach (var action in inputActions)
            {
                foreach (var subaction in GetActionSubactionPaths(actionSet, action, includeNull: true))
                {
                    appendLine("");

                    appendLine("if (XR_SUCCEEDED(result))");
                    appendLine("{");
                    indent++;
                    appendLine($"actionStateGetInfo.action = actionSet.{GetActionHandleMemberName(action.Name, conv)};");

                    if (action.UseSubactionPaths)
                    {
                        appendLine($"actionStateGetInfo.subactionPath = actionSet.{GetSubactionMemberName(subaction, conv)};");
                    }
                    else
                    {
                        appendLine($"actionStateGetInfo.subactionPath = XR_NULL_PATH;");
                    }

                    appendLine($"result = {GetActionStateUpdateFunction(action.Type)}(session, &actionStateGetInfo, &{GetActionStateMemberName(action, subaction, conv)});");

                    indent--;
                    appendLine("}");
                }
            }
            appendLine("return result;");
            indent--;
            appendLine("}");

            // Write function to get the action states for a specific subaction path.
            if (usedTopLevelPathsWithoutNull.Any())
            {
                appendLine("");
                appendLine($"SubactionStates GetSubactionStates({GetActionSetStructName(actionSet, conv)} const& actionSet, XrPath subactionPath) const");
                appendLine("{");
                indent++;
                bool firstCase = true;
                foreach (var subactionPath in usedTopLevelPathsWithoutNull)
                {
                    appendLine($"{(firstCase ? "" : "else ")}if (subactionPath == actionSet.{GetSubactionMemberName(subactionPath, conv)})");
                    appendLine("{");
                    indent++;
                    var subactionMemberVariables = inputActions
                                                   .Where(a => a.UseSubactionPaths)
                                                   .Select(a =>
                    {
                        bool hasThisSubactionBinding = GetActionSubactionPaths(actionSet, a).Any(s => s == subactionPath);
                        return(hasThisSubactionBinding ? ("&" + GetActionStateMemberName(a, subactionPath, conv)) : "nullptr");
                    });

                    appendLine($"return {{subactionPath, {string.Join(", ", subactionMemberVariables)}}};");
                    indent--;
                    appendLine("}");
                    firstCase = false;
                }

                appendLine("else");
                appendLine("{");
                indent++;
                appendLine("return {}; // Unknown subaction path.");
                indent--;
                appendLine("}");


                indent--;
                appendLine("}");
            }

            appendLine("");
            foreach (var action in inputActions)
            {
                foreach (var subaction in GetActionSubactionPaths(actionSet, action, includeNull: true))
                {
                    appendLine($"{GetActionStateStructName(action.Type)} {GetActionStateMemberName(action, subaction, conv)}{{{GetActionStateStructType(action.Type)}}};");
                }
            }

            // Write out per-subaction path member struct shortcuts.

            /*addLineIfTrue(usedTopLevelPathsWithoutNull.Any());
             * foreach (var subactionPath in usedTopLevelPathsWithoutNull)
             * {
             *  appendLine($"SubactionStates {GetSubactionMemberName(subactionPath, conv)};");
             * }*/

            indent--;
            appendLine("};");
        }
        private static void WriteActionSetStruct(StringBuilder sb, ActionSet actionSet, int indent, INamingConventionConverter conv)
        {
            Action <string> appendLineTrim = (string text) => sb.AppendLine(text.TrimEnd());
            Action <string> appendLine     = (string text) => appendLineTrim($"{new string(' ', indent * 4)}{text}");
            Action <bool>   addLineIfTrue  = (bool condition) => { if (condition)
                                                                   {
                                                                       appendLineTrim("");
                                                                   }
            };

            appendLine("");
            appendLine($"struct {GetActionSetStructName(actionSet, conv)}");
            appendLine("{");
            indent++;

            appendLine($"~{GetActionSetStructName(actionSet, conv)}()");
            appendLine("{");
            indent++;
            appendLine("if (ActionSet != XR_NULL_HANDLE)");
            appendLine("{");
            indent++;
            appendLine("(void)xrDestroyActionSet(ActionSet);");
            indent--;
            appendLine("}");
            indent--;
            appendLine("}");

            appendLine("");
            WriteActionSetInitializeMethod(sb, actionSet, indent, conv);

            foreach (var action in actionSet.Actions)
            {
                if (action.Type != ActionType.Pose)
                {
                    continue;
                }

                WriteCreateActionSpaceHelpers(sb, actionSet, action, indent, conv);
            }

            appendLine("");
            appendLine($"XrActionSet {conv.Rename("ActionSet")}{{XR_NULL_HANDLE}};");

            appendLine("");
            foreach (var action in actionSet.Actions)
            {
                appendLine($"XrAction {GetActionHandleMemberName(action.Name, conv)}{{XR_NULL_HANDLE}};");
            }

            // Write out the used subaction paths.
            var usedTopLevelPathsWithoutNull = actionSet.Actions.SelectMany(a => GetActionSubactionPaths(actionSet, a)).ToHashSet();

            addLineIfTrue(usedTopLevelPathsWithoutNull.Any());
            foreach (var subactionPath in usedTopLevelPathsWithoutNull)
            {
                appendLine($"XrPath {GetSubactionMemberName(subactionPath, conv)}{{XR_NULL_PATH}};");
            }

            indent--;
            appendLine("};");
        }