/* SPDX-FileCopyrightText: 2024 Blender Authors
 *
 * SPDX-License-Identifier: GPL-2.0-or-later */

/** \file
 * \ingroup animrig
 *
 * \brief Functionality to iterate an Action in various ways.
 */

#pragma once

#include "BLI_function_ref.hh"
#include "DNA_action_types.h"

namespace blender {

struct FCurve;
struct PointerRNA;
struct PropertyRNA;
namespace animrig {
class Action;
class Layer;
class Strip;
class Channelbag;
}  // namespace animrig

namespace animrig {

using slot_handle_t = decltype(blender::ActionSlot::handle);

/**
 * Iterates over all FCurves of the Action and executes the callback on it.
 * Works on layered and legacy actions.
 */
void foreach_fcurve_in_action(Action &action, FunctionRef<void(FCurve &fcurve)> callback);

/**
 * Iterates over all FCurves of the given slot handle in the Action and executes the callback on
 * it. This includes data that may be locked. If you want to limit this to editable data only use
 * `foreach_fcurve_in_action_slot_editable`.
 *
 * \note This could almost take a `const Action &` except for the fact that we do need to iterate
 * all FCurves in an editable way sometimes, e.g. for the pose library code.
 *
 * \note Use lambdas to have access to specific data in the callback.
 */
void foreach_fcurve_in_action_slot(Action &action,
                                   slot_handle_t handle,
                                   FunctionRef<void(FCurve &fcurve)> callback);

/**
 * Like `foreach_fcurve_in_action_slot` except any data that is not editable is skipped.
 */
void foreach_fcurve_in_action_slot_editable(Action &action,
                                            slot_handle_t handle,
                                            FunctionRef<void(FCurve &fcurve)> callback);

/**
 * Call the given callback for each Action + Slot that this ID uses.
 *
 * The following cases are visited:
 *   - Direct Action+Slot assignment.
 *   - NLA strips.
 *   - Action Constraints, both on Object and Pose Bone level.
 *
 * \param callback: The function to call for each Action+Slot used. Even when there is no slot
 * assigned, this function will be called (but then with slot_handle = Slot::unassigned). The
 * callback should return `true` to continue the foreach loop, or return `false` to stop it.
 *
 * \returns Whether the foreach loop came to a natural end. So returns `false` when the callback
 * returned `false`, and `true` otherwise.
 */
bool foreach_action_slot_use(
    const ID &animated_id,
    FunctionRef<bool(const Action &action, slot_handle_t slot_handle)> callback);

/**
 * Essentially the same as foreach_action_slot_use(), except that it provides
 * the ID as well as pointers via which the callback can modify which
 * Action/slot is assigned.
 *
 * The ID passed to the callback is always the same `animated_id` as is passed
 * to this function. The actions & slots passed to the callback are *not*
 * necessarily the direct action & slot of that ID: they can also be the action
 * & slot of an Action Constraint or NLA Strip owned by the ID.
 *
 * \see animrig::generic_assign_action
 * \see animrig::generic_assign_action_slot
 * \see animrig::generic_assign_action_slot_handle
 */
bool foreach_action_slot_use_with_references(
    ID &animated_id,
    FunctionRef<bool(ID &animated_id,
                     bAction *&action_ptr_ref,
                     slot_handle_t &slot_handle_ref,
                     char *last_slot_identifier)> callback);

/**
 * Essentially the same as foreach_action_slot_use(), except that it provides
 * the ID as well as the RNA properties via which the callback can modify which
 * Action/slot is assigned.
 *
 * The ID passed to the callback is always the same `animated_id` as is passed
 * to this function. The actions & slots passed to the callback are *not*
 * necessarily the direct action & slot of that ID: they can also be the action
 * & slot of an Action Constraint or NLA Strip owned by the ID.
 *
 * \note this function CANNOT be used to change which Action is assigned, as that makes the
 * PointerRNA/PropertyRNA values invalid.
 *
 * \see foreach_action_slot_use_with_references
 */
bool foreach_action_slot_use_with_rna(ID &animated_id,
                                      FunctionRef<bool(ID &animated_id,
                                                       bAction *action,
                                                       PointerRNA &action_slot_owner_ptr,
                                                       PropertyRNA &action_slot_prop,
                                                       char *last_slot_identifier)> callback);

}  // namespace animrig
}  // namespace blender
