#pragma once

#include <QtGlobal>

#include "WorldConfig.h"

// Which way the player is currently panning the view (REQ-UI-SCROLL). A plain
// direction rather than key state: the camera is deliberately agnostic about how
// the intent was expressed, so rebindable controls would change nothing here.
enum class PanDirection
{
    None,
    Left,
    Right
};

// The horizontal limits of the view center, in world tiles (REQ-GW-SCROLL-LIMIT):
// the view can pan left until the asteroid's left edge is centered and right until
// the enemy stations are. Both move as the game progresses — the left edge with
// asteroid expansion (REQ-GW-ASTEROID-EXPAND), the right edge as stations are
// pushed back (REQ-GW-PUSH-EXPAND) — so they are supplied per frame by the caller
// that can see the simulation, rather than queried here. That keeps the camera a
// value with no simulation dependency.
struct ScrollBounds
{
    float leftTiles;
    float rightTiles;
};

// Horizontal view position for the game world (REQ-UI-SCROLL, REQ-UI-SCROLL-SPEED).
// Works purely in world units — tiles and tiles per second, never pixels. Turning
// the resulting position into a widget transform is WorldCoordinates' job; the two
// meet only where the view feeds getViewCenterXTiles() into that transform.
class WorldCamera
{
public:
    // Both config structs are referenced rather than copied: they live inside the
    // Simulation's GameConfig, which is assigned in place on restart
    // (REQ-CFG-RELOAD), so a camera built once still picks up reloaded tuning.
    WorldCamera(const WorldScroll& scroll, const WorldRegions& regions);

    // Pans by `direction` for `elapsedMs` of wall-clock time, then clamps into
    // `bounds`. Wall clock rather than ticks because panning is presentation only
    // (REQ-UI-NO-ZOOM's sibling concern) and keeps working while the simulation is
    // paused. Clamping happens on every call, not just when panning, so the view
    // follows the bounds inward when they shrink.
    //
    // Returns true when the view center actually moved — including when it moved
    // only because the bounds did. Callers use that to refresh anything anchored to
    // the world under a stationary cursor, such as the box-select rectangle.
    bool advance(PanDirection direction, qint64 elapsedMs, ScrollBounds bounds);

    // World X (tiles) at the center of the viewport.
    float getViewCenterXTiles() const;

    // Returns the view to the start-of-run position. Deliberately does not clamp:
    // a new run's bounds are not known here, and the next advance() clamps anyway.
    void reset();

    // Pan speed at a given view center, in tiles/s (REQ-UI-SCROLL-SPEED). Public
    // because the ramp shape is the subtle part of this class and is worth testing
    // directly; advance() uses it internally. `contestZoneRightEdgeTiles` is the
    // live right-hand boundary — the same value as ScrollBounds::rightTiles — so
    // the ramp follows the front line as it is pushed.
    float getPanSpeedTilesPerSecondAt(float viewCenterXTiles,
                                      float contestZoneRightEdgeTiles) const;

private:
    const WorldScroll*  m_scroll;
    const WorldRegions* m_regions;
    float               m_viewCenterXTiles;
};
