#pragma once

#include "MayaFlux/Buffers/BufferSpec.hpp"
#include "MayaFlux/Core/ProcessingTokens.hpp"

namespace MayaFlux {

namespace Nodes {
    class Node;
    class NodeGraphManager;
    class RootNode;
    namespace Network {
        class NodeNetwork;
    }
}

namespace Buffers {
    class Buffer;
    class AudioBuffer;
    class VKBuffer;
    class RootAudioBuffer;
    class BufferManager;
    class BufferProcessor;
    class BufferProcessingChain;
}

namespace internal {
    MAYAFLUX_API std::shared_ptr<Buffers::BufferProcessor> attach_quick_process_audio(Buffers::AudioProcessingFunction processor, const std::shared_ptr<Buffers::AudioBuffer>& buffer);
    MAYAFLUX_API std::shared_ptr<Buffers::BufferProcessor> attach_quick_process_graphics(Buffers::GraphicsProcessingFunction processor, const std::shared_ptr<Buffers::VKBuffer>& buffer);
}

//-------------------------------------------------------------------------
// Node Graph Management
//-------------------------------------------------------------------------

/**
 * @brief Gets the node graph manager from the default engine
 * @return Shared pointer to the centrally managed NodeGraphManager
 *
 * Returns the node graph manager that's managed by the default engine instance.
 * All node operations using the convenience functions will use this manager.
 */
MAYAFLUX_API std::shared_ptr<Nodes::NodeGraphManager> get_node_graph_manager();

/**
 * @brief Adds a node to the root node of a specific channel
 * @param node Node to add
 * @param channel Channel index
 *
 * Adds the node as a child of the root node for the specified channel.
 * Uses the default engine's node graph manager.
 */
MAYAFLUX_API void register_audio_node(const std::shared_ptr<Nodes::Node>& node, uint32_t channel = 0);

/**
 * @brief Adds a node to the root node of specified channels
 * @param node Node to add
 * @param channels Vector of channel indices
 *
 * Adds the node as a child of the root node for the specified channels.
 * Uses the default engine's node graph manager.
 */
MAYAFLUX_API void register_audio_node(const std::shared_ptr<Nodes::Node>& node, const std::vector<uint32_t>& channels);

MAYAFLUX_API void register_node(
    const std::shared_ptr<Nodes::Node>& node,
    const Nodes::ProcessingToken& token,
    uint32_t channel = 0);

/**
 *  @breif Moves the node from its current channel(s) to the specified channel(s) with an optional fade time
 *  @param node Node to route
 *  @param channels Vector of channel indices to route the node to
 *  @param seconds_to_fade Time in seconds to fade the routing (optional, default is 1 second)
 *  @param token Processing domain to route within (default is AUDIO_RATE)
 *
 *  This method adds the specified node to the root nodes of the target channels
 *  within the given processing domain. If seconds_to_fade is greater than 0, the routing
 *  will be smoothly faded in over that time period.
 */
MAYAFLUX_API void route_node(
    const std::shared_ptr<Nodes::Node>& node,
    const std::vector<uint32_t>& channels,
    double seconds_to_fade = 1.F,
    const Nodes::ProcessingToken& token = Nodes::ProcessingToken::AUDIO_RATE);

/**
 *  @breif Moves the node from its current channel(s) to the specified channel(s) with an optional fade time
 *  @param node Node to route
 *  @param channels Vector of channel indices to route the node to
 *  @param num_samples Number of samples to fade the routing over (optional, default is 48000 samples for 1 second at 48kHz)
 *  @param token Processing domain to route within (default is AUDIO_RATE)
 *
 *  This method adds the specified node to the root nodes of the target channels
 *  within the given processing domain. If num_samples is greater than 0, the routing
 *  will be smoothly faded in over that many samples.
 */
MAYAFLUX_API void route_node(
    const std::shared_ptr<Nodes::Node>& node,
    const std::vector<uint32_t>& channels,
    uint32_t num_samples,
    const Nodes::ProcessingToken& token = Nodes::ProcessingToken::AUDIO_RATE);

/**
 * @brief Removes a node from the root node of a specific channel
 * @param node Node to remove
 * @param channel Channel index
 *
 * Removes the node from being a child of the root node for the specified channel.
 * Uses the default engine's node graph manager.
 */
MAYAFLUX_API void unregister_audio_node(const std::shared_ptr<Nodes::Node>& node, uint32_t channel = 0);

/**
 * @brief Removes a node from the root node of specified channels
 * @param node Node to remove
 * @param channels Vector of channel indices
 *
 * Removes the node from being a child of the root node for the specified channels.
 * Uses the default engine's node graph manager.
 */
MAYAFLUX_API void unregister_node(
    const std::shared_ptr<Nodes::Node>& node,
    const Nodes::ProcessingToken& token,
    uint32_t channel = 0);

/**
 * @brief Removes a node from the root node from list of channels
 * @param node Node to remove
 * @param channels Channel indices
 *
 * Removes the node from being a child of the root node for the list of channels
 * Uses the default engine's node graph manager.
 */
MAYAFLUX_API void unregister_audio_node(const std::shared_ptr<Nodes::Node>& node, const std::vector<uint32_t>& channels);

/**
 * @brief Gets the root node for a specific channel
 * @param channel Channel index
 * @return The root node for the specified channel
 *
 * The root node is the top-level node in the processing hierarchy for a channel.
 * Uses the default engine's node graph manager.
 */
MAYAFLUX_API Nodes::RootNode& get_audio_channel_root(uint32_t channel = 0);

template <typename NodeType, typename... Args>
    requires std::derived_from<NodeType, Nodes::Node>
auto create_node(Args&&... args) -> std::shared_ptr<NodeType>
{
    auto node = std::make_shared<NodeType>(std::forward<Args>(args)...);
    register_audio_node(node);
    return node;
}

//-------------------------------------------------------------------------
// Node Network Management
//-------------------------------------------------------------------------

/**
 * @brief Registers a node network with the default engine's node graph manager
 * @param network NodeNetwork to register
 *
 * Adds the node network to the default engine's node graph manager.
 * The network can then be used to manage nodes and their connections.
 */
MAYAFLUX_API void register_node_network(const std::shared_ptr<Nodes::Network::NodeNetwork>& network, const Nodes::ProcessingToken& token = Nodes::ProcessingToken::AUDIO_RATE);

/**
 * @brief Unregisters a node network from the default engine's node graph manager
 * @param network NodeNetwork to unregister
 *
 * Removes the node network from the default engine's node graph manager.
 * The network will no longer be available for managing nodes and their connections.
 */
MAYAFLUX_API void unregister_node_network(const std::shared_ptr<Nodes::Network::NodeNetwork>& network, const Nodes::ProcessingToken& token = Nodes::ProcessingToken::AUDIO_RATE);

/**
 *  @breif Moves the network from its current channel(s) to the specified channel(s) with an optional fade time
 *  @param network NodeNetwork to route
 *  @param channels Vector of channel indices to route the network to
 *  @param seconds_to_fade Time in seconds to fade the routing (optional, default is 1 second)
 *  @param token Processing domain to route within (default is AUDIO_RATE)
 *
 *  This method adds the specified network to the target channels
 *  within the given processing domain. If seconds_to_fade is greater than 0, the routing
 *  will be smoothly faded in over that time period.
 */
MAYAFLUX_API void route_network(
    const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
    const std::vector<uint32_t>& channels,
    double seconds_to_fade = 1.F,
    const Nodes::ProcessingToken& token = Nodes::ProcessingToken::AUDIO_RATE);

/**
 *  @breif Moves the node from its current channel(s) to the specified channel(s) with an optional fade time
 *  @param network NodeNetwork to route
 *  @param channels Vector of channel indices to route the node to
 *  @param num_samples Number of samples to fade the routing over (optional, default is 48000 samples for 1 second at 48kHz)
 *  @param token Processing domain to route within (default is AUDIO_RATE)
 *
 *  This method adds the specified network to the target channels
 *  within the given processing domain. If num_samples is greater than 0, the routing
 *  will be smoothly faded in over that many samples.
 */
MAYAFLUX_API void route_network(
    const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
    const std::vector<uint32_t>& channels,
    uint32_t num_samples,
    const Nodes::ProcessingToken& token = Nodes::ProcessingToken::AUDIO_RATE);

/**
 * @brief Creates a new node network
 * @return Shared pointer to the created NodeNetwork
 *
 * This function creates a new node network that can be used to manage
 * complex node graphs with multiple channels and processing domains.
 */

template <typename NodeNetworkType, typename... Args>
    requires std::derived_from<NodeNetworkType, Nodes::Network::NodeNetwork>
auto create_node_network(Args&&... args) -> std::shared_ptr<NodeNetworkType>
{
    auto network = std::make_shared<NodeNetworkType>(std::forward<Args>(args)...);
    register_node_network(network);
    return network;
}

//-------------------------------------------------------------------------
// Buffer Management
//-------------------------------------------------------------------------

/**
 * @brief Gets the buffer manager from the default engine
 * @return Shared pointer to the centrally managed BufferManager
 *
 * Returns the buffer manager that's managed by the default engine instance.
 * All buffer operations using the convenience functions will use this manager.
 */
MAYAFLUX_API std::shared_ptr<Buffers::BufferManager> get_buffer_manager();

/**
 * @brief Adds a processor to a specific buffer
 * @param processor Processor to add
 * @param buffer Buffer to add the processor to
 *
 * Adds the processor to the specified buffer's processing chain.
 * Uses the default engine's buffer manager.
 */
MAYAFLUX_API void add_processor(const std::shared_ptr<Buffers::BufferProcessor>& processor, const std::shared_ptr<Buffers::Buffer>& buffer, Buffers::ProcessingToken token = Buffers::ProcessingToken::AUDIO_BACKEND);

/**
 * @brief Adds a processor to all buffers in a specific channel
 * @param processor Processor to add
 * @param token Processing domain
 * @param channel Channel index
 *
 * Adds the processor to all buffers associated with the specified channel
 * in the given processing domain.
 * Uses the default engine's buffer manager.
 */
MAYAFLUX_API void add_processor(const std::shared_ptr<Buffers::BufferProcessor>& processor, Buffers::ProcessingToken token, uint32_t channel);

/**
 * @brief Adds a processor to all buffers in a processing domain
 * @param processor Processor to add
 * @param token Processing domain
 *
 * Adds the processor to all buffers associated with the given processing domain.
 * Uses the default engine's buffer manager.
 */
MAYAFLUX_API void add_processor(const std::shared_ptr<Buffers::BufferProcessor>& processor, Buffers::ProcessingToken token = Buffers::ProcessingToken::AUDIO_BACKEND);

/**
 * @brief Removes a processor from a specific buffer
 * @param processor Processor to remove
 * @param buffer Buffer to remove the processor from
 *
 * Removes the processor from the specified buffer's processing chain.
 * Uses the default engine's buffer manager.
 */
void remove_processor(const std::shared_ptr<Buffers::BufferProcessor>& processor, const std::shared_ptr<Buffers::Buffer>& buffer);

/**
 * @brief Removes a processor from all buffers in a specific channel
 * @param processor Processor to remove
 * @param token Processing domain
 * @param channel Channel index
 *
 * Removes the processor from all buffers associated with the specified channel
 * in the given processing domain.
 * Uses the default engine's buffer manager.
 */
void remove_processor(const std::shared_ptr<Buffers::BufferProcessor>& processor, uint32_t channel, Buffers::ProcessingToken token = Buffers::ProcessingToken::AUDIO_BACKEND);

/**
 * @brief Removes a processor from all buffers in a processing domain
 * @param processor Processor to remove
 * @param token Processing domain
 *
 * Removes the processor from all buffers associated with the given processing domain.
 * Uses the default engine's buffer manager.
 */
void remove_processor(const std::shared_ptr<Buffers::BufferProcessor>& processor, Buffers::ProcessingToken token = Buffers::ProcessingToken::AUDIO_BACKEND);

/**
 * @brief Registers an AudioBuffer with the default engine's buffer manager
 * @param buffer AudioBuffer to register
 * @param channel Channel index to associate with the buffer (default: 0)
 *
 * Adds the buffer to the default engine's buffer management system, enabling
 * it to participate in the audio processing pipeline. The buffer will be
 * processed during each audio cycle according to its configuration.
 * Multiple buffers can be registered to the same channel for layered processing.
 */
MAYAFLUX_API void register_audio_buffer(const std::shared_ptr<Buffers::AudioBuffer>& buffer, uint32_t channel = 0);

/**
 * @brief Unregisters an AudioBuffer from the default engine's buffer manager
 * @param buffer AudioBuffer to unregister
 * @param channel Channel index the buffer was associated with (default: 0)
 *
 * Removes the buffer from the default engine's buffer management system.
 * The buffer will no longer participate in audio processing cycles.
 * This is essential for clean shutdown and preventing processing of
 * destroyed or invalid buffers.
 */
MAYAFLUX_API void unregister_audio_buffer(const std::shared_ptr<Buffers::AudioBuffer>& buffer, uint32_t channel = 0);

/**
 * @brief creates a new buffer of the specified type and registers it
 * @tparam BufferType Type of buffer to create (must be derived from AudioBuffer)
 * @tparam Args Constructor argument types
 * @param channel Channel index to create the buffer for
 * @param buffer_size Size of the buffer in processing units
 */
template <typename BufferType, typename... Args>
    requires std::derived_from<BufferType, Buffers::AudioBuffer>
auto create_buffer(uint32_t channel, uint32_t buffer_size, Args&&... args) -> std::shared_ptr<BufferType>
{
    auto buffer = std::make_shared<BufferType>(channel, buffer_size, std::forward<Args>(args)...);
    register_audio_buffer(buffer, channel);
    return buffer;
}

/**
 * @brief Registers a VKBuffer with the default engine's buffer manager
 * @param buffer VKBuffer to register
 *
 * Adds the buffer to the default engine's buffer management system, enabling
 * it to participate in the graphics processing pipeline.
 */
MAYAFLUX_API void register_graphics_buffer(const std::shared_ptr<Buffers::VKBuffer>& buffer, Buffers::ProcessingToken token = Buffers::ProcessingToken::GRAPHICS_BACKEND);

/**
 * @brief Unregisters a VKBuffer from the default engine's buffer manager
 * @param buffer VKBuffer to unregister
 *
 * Removes the buffer from the default engine's buffer management system.
 * The buffer will no longer participate in graphics processing cycles.
 */
MAYAFLUX_API void unregister_graphics_buffer(const std::shared_ptr<Buffers::VKBuffer>& buffer);

/**
 * @brief creates a new graphics buffer of the specified type and registers it
 * @tparam BufferType Type of buffer to create (must be derived from VKBuffer)
 * @tparam Args Constructor argument types
 * @param args Constructor arguments for the buffer
 * @return Shared pointer to the created buffer
 */
template <typename BufferType, typename... Args>
    requires std::derived_from<BufferType, Buffers::VKBuffer>
auto create_buffer(Args&&... args) -> std::shared_ptr<BufferType>
{
    auto buffer = std::make_shared<BufferType>(std::forward<Args>(args)...);
    register_graphics_buffer(buffer);
    return buffer;
}

/**
 *  @brief Creates a new processor and adds it to a buffer
 * @tparam ProcessorType Type of processor to create (must be derived from BufferProcessor)
 * @tparam Args Constructor argument types
 * @param buffer Buffer to add the processor to
 * @param args Constructor arguments for the processor
 * @return Shared pointer to the created processor
 *
 * This function creates a new processor of the specified type, initializes it with the provided arguments,
 * and adds it to the specified buffer's processing chain.
 */
template <typename ProcessorType, typename... Args>
    requires std::derived_from<ProcessorType, Buffers::BufferProcessor>
auto create_processor(const std::shared_ptr<Buffers::AudioBuffer> buffer, Args&&... args) -> std::shared_ptr<ProcessorType>
{
    auto processor = std::make_shared<ProcessorType>(std::forward<Args>(args)...);
    add_processor(processor, buffer);
    return processor;
}

template <typename ProcessorType, typename... Args>
    requires std::derived_from<ProcessorType, Buffers::BufferProcessor>
auto create_processor(const std::shared_ptr<Buffers::VKBuffer> buffer, Args&&... args) -> std::shared_ptr<ProcessorType>
{
    auto processor = std::make_shared<ProcessorType>(std::forward<Args>(args)...);
    add_processor(processor, buffer, Buffers::ProcessingToken::GRAPHICS_BACKEND);
    return processor;
}

/**
 * @brief Creates a new processing chain for the default engine
 * @return Shared pointer to the created BufferProcessingChain
 *
 * This function creates a new processing chain that can be used to manage
 * audio processing for buffers. The chain can be customized with various
 * processors and is managed by the default engine's buffer manager.
 */
MAYAFLUX_API std::shared_ptr<Buffers::BufferProcessingChain> create_processing_chain();

/**
 * @brief Gets the audio buffer for a specific channel
 * @param channel Channel index
 * @return Reference to the channel's AudioBuffer
 *
 * Returns the buffer from the default engine's buffer manager.
 */
MAYAFLUX_API Buffers::RootAudioBuffer& get_root_audio_buffer(uint32_t channel);

/**
 * @brief Connects a node to a specific output channel
 * @param node Node to connect
 * @param channel_index Channel index to connect to
 * @param mix Mix level (0.0 to 1.0) for blending node output with existing channel content
 * @param clear_before Whether to clear the channel buffer before adding node output
 *
 * Uses the default engine's buffer manager and node graph manager.
 */
MAYAFLUX_API void connect_node_to_channel(const std::shared_ptr<Nodes::Node>& node, uint32_t channel_index = 0, float mix = 0.5F, bool clear_before = false);

/**
 * @brief Connects a node to a specific buffer
 * @param node Node to connect
 * @param buffer Buffer to connect to
 * @param mix Mix level (0.0 to 1.0) for blending node output with existing buffer content
 * @param clear_before Whether to clear the buffer before adding node output
 *
 * Uses the default engine's node graph manager.
 */
MAYAFLUX_API void connect_node_to_buffer(const std::shared_ptr<Nodes::Node>& node, const std::shared_ptr<Buffers::AudioBuffer>& buffer, float mix = 0.5F, bool clear_before = true);

//-------------------------------------------------------------------------
// Audio Processing
//-------------------------------------------------------------------------

/**
 * @brief Attaches a processing function to a specific channel
 * @param processor Function to process the buffer
 * @param channel_id Channel index to process
 *
 * The processor will be called during the default engine's audio processing cycle
 * and will operate on the specified output channel buffer.
 */
MAYAFLUX_API std::shared_ptr<Buffers::BufferProcessor> attach_quick_process(Buffers::AudioProcessingFunction processor, unsigned int channel_id = 0);

// Template overloads for auto lambdas
template <typename F>
std::shared_ptr<Buffers::BufferProcessor> attach_quick_process(
    F&& processor,
    const std::shared_ptr<Buffers::AudioBuffer>& buffer)
{
    return internal::attach_quick_process_audio(
        Buffers::AudioProcessingFunction { std::forward<F>(processor) },
        buffer);
}

template <typename F>
std::shared_ptr<Buffers::BufferProcessor> attach_quick_process(
    F&& processor,
    const std::shared_ptr<Buffers::VKBuffer>& buffer)
{
    return internal::attach_quick_process_graphics(
        Buffers::GraphicsProcessingFunction { std::forward<F>(processor) },
        buffer);
}

/**
 * @brief Reads audio data from the default input source into a buffer
 * @param buffer Buffer to read audio data into
 * @param channel Channel index to read from (default: 0)
 *
 * Reads audio data from the default input source (e.g., microphone)
 * and fills the provided AudioBuffer with the captured audio samples.
 * This function is typically used to capture live audio input for processing.
 */
MAYAFLUX_API void read_from_audio_input(const std::shared_ptr<Buffers::AudioBuffer>& buffer, uint32_t channel = 0);

/**
 * @brief Stops reading audio data from the default input source
 * @param buffer Buffer to stop reading audio data from
 * @param channel Channel index to stop reading from (default: 0)
 */
MAYAFLUX_API void detach_from_audio_input(const std::shared_ptr<Buffers::AudioBuffer>& buffer, uint32_t channel = 0);

/**
 * @brief Creates a new AudioBuffer for input listening
 * @param channel Channel index to create the buffer for (default: 0)
 * @param add_to_output Whether to add this buffer to the output channel (default: false)
 * @return Shared pointer to the created AudioBuffer
 *
 * This function creates a new AudioBuffer that can be used to listen to audio input.
 * If `add_to_output` is true, the buffer will be automatically added to the output channel
 * for processing.
 */
MAYAFLUX_API std::shared_ptr<Buffers::AudioBuffer> create_input_listener_buffer(uint32_t channel = 0, bool add_to_output = false);

/**
 * @brief Clones a buffer to multiple channels
 * @param buffer Buffer to clone
 * @param channels Vector of channel indices to clone to
 *
 * Creates independent copies of the buffer for each specified channel.
 * Each clone maintains the same data, processors, and processing chain,
 * but operates independently on its assigned channel.
 * Uses the default engine's buffer manager.
 */
MAYAFLUX_API std::vector<std::shared_ptr<Buffers::AudioBuffer>> clone_buffer_to_channels(const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    const std::vector<uint32_t>& channels);

MAYAFLUX_API std::vector<std::shared_ptr<Buffers::AudioBuffer>> clone_buffer_to_channels(
    const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    const std::vector<uint32_t>& channels,
    const Buffers::ProcessingToken& token);
/**
 * @brief Supplies a buffer to a single channel with mixing
 * @param buffer Source buffer to supply
 * @param channel Target channel index
 * @param mix Mix level (default: 1.0)
 *
 * Convenience wrapper for single-channel buffer supply operations.
 */
MAYAFLUX_API void supply_buffer_to_channel(const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    uint32_t channel,
    double mix = 1.0);

/**
 * @brief Supplies a buffer to multiple channels with mixing
 * @param buffer Source buffer to supply
 * @param channels Vector of channel indices to supply to
 * @param mix Mix level for each channel (default: 1.0)
 *
 * Efficiently routes a single buffer's output to multiple channels using
 * the MixProcessor system. This is ideal for sending the same signal to
 * multiple outputs without duplicating processing.
 */
MAYAFLUX_API void supply_buffer_to_channels(const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    const std::vector<uint32_t>& channels,
    double mix = 1.0);

/**
 * @brief Removes a supplied buffer from multiple channels
 * @param buffer Buffer to remove from supply chains
 * @param channels channel index to remove from
 *
 * Efficiently removes a buffer from channel mix processor.
 */
MAYAFLUX_API void remove_supplied_buffer_from_channel(const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    const uint32_t channel);

/**
 * @brief Removes a supplied buffer from multiple channels
 * @param buffer Buffer to remove from supply chains
 * @param channels Vector of channel indices to remove from
 *
 * Efficiently removes a buffer from multiple channel mix processors.
 */
MAYAFLUX_API void remove_supplied_buffer_from_channels(const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    const std::vector<uint32_t>& channels);

/**
 * @brief Routes a buffer from its current channel to a target channel with fade
 * @param buffer Buffer to route
 * @param target_channel Destination channel
 * @param seconds_to_fade Crossfade duration in seconds
 * @param token Processing domain
 */
MAYAFLUX_API void route_buffer(
    const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    uint32_t target_channel,
    double seconds_to_fade = 1.0,
    const Buffers::ProcessingToken& token = Buffers::ProcessingToken::AUDIO_BACKEND);

/**
 * @brief Routes a buffer from its current channel to a target channel with fade
 * @param buffer Buffer to route
 * @param target_channel Destination channel
 * @param num_blocks Crossfade duration in processing blocks
 * @param token Processing domain
 *
 * NOTE: num_blocks is in processing blocks, not samples. The actual time duration will depend on the block size and sample rate.
 */
MAYAFLUX_API void route_buffer(
    const std::shared_ptr<Buffers::AudioBuffer>& buffer,
    uint32_t target_channel,
    uint32_t num_blocks,
    const Buffers::ProcessingToken& token = Buffers::ProcessingToken::AUDIO_BACKEND);

/**
 * @brief Connects two nodes in series (pipeline operator)
 * @param lhs Source node (may already be a ChainNode)
 * @param rhs Target node (may already be a ChainNode)
 * @return A ChainNode containing the flattened sequence
 *
 * If lhs is already a ChainNode, rhs is appended to its sequence
 * rather than creating a nested chain.
 *
 * Uses the default engine's NodeGraphManager for registration.
 * For manual construction without registration, create ChainNode directly
 * with no manager argument.
 *
 * ```cpp
 * auto chain = generator >> transformer >> output;
 * ```
 */
MAYAFLUX_API std::shared_ptr<Nodes::Node> operator>>(const std::shared_ptr<Nodes::Node>& lhs, const std::shared_ptr<Nodes::Node>& rhs);

/**
 * @brief Combines two nodes in parallel (addition)
 * @param lhs First node
 * @param rhs Second node
 * @return A BinaryOpNode that sums both outputs
 *
 * Creates a new node that processes both input nodes and sums their outputs.
 * This allows for mixing multiple data sources or transformations:
 *
 * ```cpp
 * auto combined = primary_source + secondary_source;
 * ```
 *
 * The resulting node takes an input, passes it to both source nodes,
 * and returns the sum of their outputs.
 */
MAYAFLUX_API std::shared_ptr<Nodes::Node> operator+(const std::shared_ptr<Nodes::Node>& lhs, const std::shared_ptr<Nodes::Node>& rhs);

/**
 * @brief Combines two nodes in parallel (multiplication)
 * @param lhs First node
 * @param rhs Second node
 * @return A BinaryOpNode that multiplies both outputs
 *
 * Creates a new node that processes both input nodes and multiplies their outputs.
 * This is useful for amplitude modulation, scaling operations, and other
 * multiplicative transformations:
 *
 * ```cpp
 * auto modulated = carrier * modulator;
 * ```
 *
 * The resulting node takes an input, passes it to both source nodes,
 * and returns the product of their outputs.
 */
MAYAFLUX_API std::shared_ptr<Nodes::Node> operator*(const std::shared_ptr<Nodes::Node>& lhs, const std::shared_ptr<Nodes::Node>& rhs);

}
