#pragma once

namespace MayaFlux::Registry::Service {

/**
 * @brief Backend display and presentation service interface
 *
 * Manages window surfaces, swapchains, and frame presentation.
 * Handles window resize events and ensures proper surface recreation.
 */
struct MAYAFLUX_API DisplayService {

    /**
     * @brief Submit a primary command buffer and present the frame
     * @param window_handle Window handle
     * @param primary_command_buffer The primary command buffer (uint64_t bits of vk::CommandBuffer)
     *
     * Handles semaphore choreography: waits on image_available, signals render_finished,
     * then presents. Must be called after acquire_next_swapchain_image().
     */
    std::function<void(const std::shared_ptr<void>&, uint64_t)> submit_and_present;

    /**
     * @brief Wait for all GPU operations to complete
     *
     * Blocks until all submitted command buffers have finished execution.
     * Used for synchronization before shutdown or major state changes.
     * Can significantly impact performance - use sparingly.
     */
    std::function<void()> wait_idle;

    /**
     * @brief Resize rendering surface for a window
     * @param window_handle Window to resize
     * @param width New width in pixels
     * @param height New height in pixels
     *
     * Recreates swapchain and associated framebuffers for new dimensions.
     * Must be called when window size changes to avoid rendering artifacts.
     * Automatically waits for idle before reconstruction.
     */
    std::function<void(const std::shared_ptr<void>&, uint32_t, uint32_t)> resize_surface;

    /**
     * @brief Get current swapchain image count
     * @param window_handle Window handle
     * @return Number of images in the swapchain
     *
     * Useful for allocating per-frame resources. Typically 2-3 images
     * for double/triple buffering.
     */
    std::function<uint32_t(const std::shared_ptr<void>&)> get_swapchain_image_count;

    /**
     * @brief Acquire the next swapchain image for a window
     * @param window_handle Window handle
     * @return Index of the acquired swapchain image
     *
     * Must be called before get_current_image_view() for dynamic rendering.
     * Stores the acquired image index internally for subsequent calls.
     */
    std::function<uint64_t(const std::shared_ptr<void>&)> acquire_next_swapchain_image;

    /**
     * @brief Get actual swapchain format for a window
     * @param window_handle Window handle
     * @return Vulkan format (vk::Format cast to uint32_t)
     *
     * Returns the actual format used by the window's swapchain.
     * Used to ensure multiple dynamic render calls are compatible
     */
    std::function<int(const std::shared_ptr<void>&)> get_swapchain_format;

    /**
     * @brief Get swapchain extent for a window
     * @param window_handle Window handle
     * @param out_width Output parameter for width
     * @param out_height Output parameter for height
     *
     * Retrieves current swapchain dimensions. Sets out_width and out_height
     * to 0 if window not registered or swapchain unavailable.
     */
    std::function<void(const std::shared_ptr<void>&, uint32_t&, uint32_t&)> get_swapchain_extent;

    /**
     * @brief Get current swapchain image view for rendering
     * @param window_handle Window handle
     * @return vk::ImageView cast to void*
     *
     * Returns the image view for the currently acquired swapchain image.
     * Used with dynamic rendering.
     */
    std::function<void*(const std::shared_ptr<void>&)> get_current_image_view;

    /**
     * @brief Get the VkImage bits for the most recently acquired swapchain image.
     * @param window_handle Window handle.
     * @return VkImage cast to uint64_t, or 0 if no image has been acquired yet.
     *
     * Safe to call after acquire_next_swapchain_image() has been invoked for
     * the current frame. Does not advance the frame index or wait on any fence.
     * Intended for read-only operations such as pixel readback.
     */
    std::function<uint64_t(const std::shared_ptr<void>&)> get_current_swapchain_image;

    /**
     * @brief Copy a sub-rectangle of the last presented swapchain image into
     *        a caller-supplied host buffer.
     *
     * Internally calls BackendResourceManager::download_image_data with
     * restore_layout = ePresentSrcKHR and restore_stage = eBottomOfPipe,
     * which are the correct values for a swapchain image post-present.
     *
     * The destination buffer must be at least
     * pixel_width * pixel_height * bytes_per_pixel bytes.
     *
     * @param window_handle  Window whose last presented image is the source.
     * @param dst            Host-visible destination pointer.
     * @param x_offset       Left edge of the source rectangle in pixels.
     * @param y_offset       Top edge of the source rectangle in pixels.
     * @param pixel_width    Width of the rectangle in pixels.
     * @param pixel_height   Height of the rectangle in pixels.
     * @param byte_count     Total bytes to read (width * height * bpp).
     * @return true if the copy completed successfully.
     */
    std::function<bool(
        const std::shared_ptr<void>& window_handle,
        void* dst,
        uint32_t x_offset,
        uint32_t y_offset,
        uint32_t pixel_width,
        uint32_t pixel_height,
        size_t byte_count)>
        readback_swapchain_region;

    /**
     * @brief Ensure a depth attachment image exists for the window
     * @param window_handle Window handle
     *
     * Lazily creates a D32_SFLOAT depth image matching the current
     * swapchain extent. Recreates if extent has changed. No-op if
     * depth image already exists at correct size.
     */
    std::function<void(const std::shared_ptr<void>&)> ensure_depth_attachment;

    /**
     * @brief Get depth image view for the window
     * @param window_handle Window handle
     * @return vk::ImageView cast to void*, or nullptr if no depth image
     *
     * Returns nullptr if ensure_depth_attachment has not been called
     * or if depth image creation failed.
     */
    std::function<void*(const std::shared_ptr<void>&)> get_depth_image_view;

    /**
     * @brief Get depth image format for the window
     * @param window_handle Window handle
     * @return vk::Format cast to uint32_t, or eUndefined if no depth image
     */
    std::function<uint32_t(const std::shared_ptr<void>&)> get_depth_format;

    /**
     * @brief Returns the last completed full-surface pixel readback for a window.
     *
     * Published by the per-window readback thread once a capture copy
     * completes. Lock-free: the readback thread stores the buffer with
     * release, callers load with acquire via an atomic shared_ptr, so the
     * pointer is safe to read from any thread. Returns nullptr if no frame
     * has completed yet or capture is unavailable on this surface.
     *
     * Raw bytes in swapchain pixel format. Width and height match the
     * current swapchain extent from get_swapchain_extent().
     */
    std::function<std::shared_ptr<std::vector<uint8_t>>(
        const std::shared_ptr<void>& window_handle)>
        get_last_frame;

    /**
     * @brief Register a per-frame observer for a window's captured frames.
     *
     * The observer fires on the window's readback thread once per captured
     * frame, receiving the published frame buffer, its width and height, and
     * the swapchain format as uint32_t. Fires only for capture-enabled
     * windows. The observer must not block; post to a queue for further work.
     *
     * Requires the window's capture state to exist, which it does once the
     * window has completed its first captured frame. Returns 0 if capture is
     * not enabled or no frame has been captured yet; retry after a frame.
     *
     * @return Opaque non-zero id for unregister_frame_observer, or 0 on failure.
     */
    std::function<uint32_t(
        const std::shared_ptr<void>& window_handle,
        std::function<void(
            const std::shared_ptr<std::vector<uint8_t>>&,
            uint32_t, uint32_t, uint32_t)>)>
        register_frame_observer;

    /**
     * @brief Unregister a previously registered per-frame observer.
     *
     * Safe from any thread, including from within the observer callback.
     *
     * @param window_handle Window the observer was registered on.
     * @param id Id returned by register_frame_observer.
     */
    std::function<void(const std::shared_ptr<void>&, uint32_t)>
        unregister_frame_observer;
};

} // namespace MayaFlux::Registry::Services
