#pragma once

#include "MayaFlux/Kinesis/Viewport/NavigationMappings.hpp"
#include "MayaFlux/Kinesis/Viewport/NavigationState.hpp"
#include "MayaFlux/Kinesis/Viewport/OrbitState.hpp"
#include "MayaFlux/Kinesis/Viewport/PanZoom2DState.hpp"

namespace MayaFlux::Buffers {
class RenderProcessor;
}

namespace MayaFlux::Core {
class Window;
}

namespace MayaFlux {

/// @brief Alias for backwards compatibility; prefer Kinesis::NavigationConfig in new code.
using ViewportPresetConfig = Kinesis::NavigationConfig;

/**
 * @enum ViewportPresetMode
 * @brief Selects which navigation controller bind_viewport_preset installs.
 *
 * Each mode registers a distinct set of event handlers and may interpret
 * ViewportPresetConfig fields differently. Modes not yet implemented emit a
 * runtime error and return without binding.
 */
enum class ViewportPresetMode : uint8_t {
    Fly, ///< First-person fly: WASD/QE translate, RMB drag yaw/pitch, scroll dolly, KP ortho snaps
    Orbit, ///< Tumble around a focal point (not yet implemented)
    PanZoom2D, ///< Orthographic 2D pan and zoom (not yet implemented)
    Screenspace, ///< Perspective pan in camera's local right/up plane, scroll dolly, no rotation
    // Trackball, ///< Virtual trackball (not yet implemented)
};

/**
 * @brief Bind a navigation preset to a window and render processor using default settings.
 *
 * Convenience wrapper that constructs a default config for the selected mode
 * and forwards to the corresponding bind_*_preset function. To tune the
 * controller, call bind_fly_preset(), bind_orbit_preset(), or
 * bind_pan_zoom_preset() directly.
 *
 * Registers event handlers under the prefix "vp_<name>_".
 *
 * Mode defaults:
 *   Fly       - WASD/QE translate, RMB drag yaw/pitch, scroll dolly, KP ortho snaps
 *   Orbit     - MMB rotate around focal point, Shift+MMB pan, scroll dolly, KP ortho snaps
 *   PanZoom2D - MMB drag pan, scroll zoom, orthographic projection
 *
 * @param window     Window supplying input events
 * @param processor  RenderProcessor that receives the ViewTransform source
 * @param mode       Navigation controller to install, defaults to Fly
 * @param name       Unique prefix for registered events; must be unique per window
 */
MAYAFLUX_API void bind_viewport_preset(
    const std::shared_ptr<Core::Window>& window,
    const std::shared_ptr<Buffers::RenderProcessor>& processor,
    ViewportPresetMode mode = ViewportPresetMode::Fly,
    const std::string& name = "default");

/**
 * @brief Bind a navigation preset to all RenderProcessors currently registered
 *        against a window using default settings.
 *
 * Calls bind_viewport_preset(window, rp, mode, name) for every buffer
 * registered with the window at call time that returns a non-null RenderProcessor.
 * Buffers registered after this call are not covered.
 *
 * @param window  Window supplying input events
 * @param mode    Navigation controller to install, defaults to Fly
 * @param name    Preset name forwarded to bind_viewport_preset()
 */
MAYAFLUX_API void bind_viewport_preset(
    const std::shared_ptr<Core::Window>& window,
    ViewportPresetMode mode = ViewportPresetMode::Fly,
    const std::string& name = "default");

/**
 * @brief Bind the fly navigation controller to a window and render processor.
 *
 * Registers event handlers under the prefix "vp_<name>_". Key assignments
 * default to WASD/QE translation with KP ortho snaps; pass a FlyKeyMap to
 * override any or all bindings. Ortho snap slots set to std::nullopt are
 * silently skipped.
 *
 * @param window     Window supplying input events
 * @param processor  RenderProcessor that receives the ViewTransform source
 * @param config     Camera/motion tuning parameters
 * @param key_map    Key assignments; defaults to WASD/QE + KP snaps
 * @param name       Unique prefix for registered events; must be unique per window
 */
MAYAFLUX_API void bind_fly_preset(
    const std::shared_ptr<Core::Window>& window,
    const std::shared_ptr<Buffers::RenderProcessor>& processor,
    const ViewportPresetConfig& config = {},
    const Kinesis::FlyKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Bind the fly navigation controller to all RenderProcessors currently
 *        registered against a window.
 *
 * Calls bind_fly_preset(window, rp, config, key_map, name) for every buffer
 * registered with the window at call time that returns a non-null RenderProcessor.
 * Buffers registered after this call are not covered.
 *
 * @param window   Window supplying input events
 * @param config   Camera/motion tuning parameters
 * @param key_map  Key assignments; defaults to WASD/QE + KP snaps
 * @param name     Preset name forwarded to bind_fly_preset()
 */
MAYAFLUX_API void bind_fly_preset(
    const std::shared_ptr<Core::Window>& window,
    const ViewportPresetConfig& config = {},
    const Kinesis::FlyKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Bind the orbit navigation controller to a window and render processor.
 *
 * Registers event handlers under the prefix "vp_<name>_". MMB drag rotates
 * around the focal point; holding key_map.pan_modifier during MMB drag pans
 * the focal point instead. Scroll dollies the distance. Ortho snap slots set
 * to std::nullopt are silently skipped.
 *
 * @param window     Window supplying input events
 * @param processor  RenderProcessor that receives the ViewTransform source
 * @param config     Orbit tuning parameters
 * @param key_map    Key assignments; defaults to Shift pan modifier + KP snaps
 * @param name       Unique prefix for registered events; must be unique per window
 */
MAYAFLUX_API void bind_orbit_preset(
    const std::shared_ptr<Core::Window>& window,
    const std::shared_ptr<Buffers::RenderProcessor>& processor,
    const Kinesis::OrbitConfig& config = {},
    const Kinesis::OrbitKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Bind the orbit navigation controller to all RenderProcessors currently
 *        registered against a window.
 *
 * @param window   Window supplying input events
 * @param config   Orbit tuning parameters
 * @param key_map  Key assignments
 * @param name     Preset name forwarded to the per-processor overload
 */
MAYAFLUX_API void bind_orbit_preset(
    const std::shared_ptr<Core::Window>& window,
    const Kinesis::OrbitConfig& config = {},
    const Kinesis::OrbitKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Bind the 2D pan/zoom controller to a window and render processor.
 *
 * Drag pans the view; scroll zooms by scaling the orthographic half-height.
 * View matrix is always identity; only the orthographic projection changes.
 *
 * @param window     Window supplying input events
 * @param processor  RenderProcessor that receives the ViewTransform source
 * @param config     Pan/zoom tuning parameters
 * @param key_map    Input assignments; defaults to MMB drag
 * @param name       Unique prefix for registered events; must be unique per window
 */
MAYAFLUX_API void bind_pan_zoom_preset(
    const std::shared_ptr<Core::Window>& window,
    const std::shared_ptr<Buffers::RenderProcessor>& processor,
    const Kinesis::PanZoom2DConfig& config = {},
    const Kinesis::PanZoom2DKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Bind the 2D pan/zoom controller to all RenderProcessors currently
 *        registered against a window.
 *
 * @param window   Window supplying input events
 * @param config   Pan/zoom tuning parameters
 * @param key_map  Input assignments
 * @param name     Preset name forwarded to the per-processor overload
 */
MAYAFLUX_API void bind_pan_zoom_preset(
    const std::shared_ptr<Core::Window>& window,
    const Kinesis::PanZoom2DConfig& config = {},
    const Kinesis::PanZoom2DKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Bind the screenspace navigation controller to a window and render processor.
 *
 * Drag translates the eye along the camera's local right and up axes derived
 * from the current yaw and pitch. Rotation is never modified. Scroll dollies
 * along the forward vector. Perspective projection is preserved throughout.
 *
 * To set the initial viewing direction, supply a NavigationConfig with
 * initial_eye and initial_target; yaw and pitch are derived from those on
 * construction exactly as in bind_fly_preset.
 *
 * @param window     Window supplying input events
 * @param processor  RenderProcessor that receives the ViewTransform source
 * @param config     Camera tuning parameters; move_speed is unused
 * @param key_map    Input assignments; defaults to RMB drag
 * @param name       Unique prefix for registered events; must be unique per window
 */
MAYAFLUX_API void bind_screenspace_preset(
    const std::shared_ptr<Core::Window>& window,
    const std::shared_ptr<Buffers::RenderProcessor>& processor,
    const Kinesis::NavigationConfig& config = {},
    const Kinesis::ScreenspaceKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Bind the screenspace navigation controller to all RenderProcessors
 *        currently registered against a window.
 *
 * @param window   Window supplying input events
 * @param config   Camera tuning parameters
 * @param key_map  Input assignments
 * @param name     Preset name forwarded to the per-processor overload
 */
MAYAFLUX_API void bind_screenspace_preset(
    const std::shared_ptr<Core::Window>& window,
    const Kinesis::NavigationConfig& config = {},
    const Kinesis::ScreenspaceKeyMap& key_map = {},
    const std::string& name = "default");

/**
 * @brief Cancel all event handlers registered by bind_viewport_preset() and
 *        restore the window input config to its state before bind was called.
 *
 * @param window  Window passed to bind_viewport_preset()
 * @param name    Name passed to bind_viewport_preset()
 */
MAYAFLUX_API void unbind_viewport_preset(
    const std::shared_ptr<Core::Window>& window,
    const std::string& name = "default");

} // namespace MayaFlux
