#pragma once

#include "Axis_State_Strategy.hpp"
#include "Axis_Builder.h"

#include <renderive/base/property/Attach_Builder.hpp>
#include <renderive/base/property/Property.hpp>

#include <cmath>
#include <stdexcept>

namespace renderive {

struct Axis_Coordinate_Range_Validator {
    void operator()(const Range& value) const {
        if (!std::isfinite(value.origin) || !std::isfinite(value.target) || !(value.size() > 0.0))
            throw std::invalid_argument("axis coordinate range must be finite and non-empty");
    }
};

struct Axis_Properties : Axis_Base_Properties {
    Validated_Value<Range, Axis_Coordinate_Range_Validator> coordinates{Range{0.0, 20.0}};
    Range_Value<int, 0, 12> precision{2};
    bool wheel{};
    bool drag{};
};

namespace detail {

struct Axis_Interaction_State {
    bool dragging{};
    PointF last_pointer{};
};

class LIB_DECL Axis_Control : public Axis_State_Strategy<Axis_Properties>, public Event_Handler {
public:
    explicit Axis_Control(const Axis_Properties& properties);
    void handle_event(const Event& event) override;

    void publish() override;
    [[nodiscard]] std::uint64_t state_revision() const override;

private:
    struct Interaction_Base {};
    using Interaction_State = Double_State_Strategy<Interaction_Base, Axis_Interaction_State>;
protected:
    [[nodiscard]] Range coordinate_range(const Axis_Properties& state) const noexcept override;
private:
    Interaction_State interaction_;
};

} // namespace detail

using Axis = Attach_Builder<detail::Axis_Control, Axis_Properties, detail::Axis_Renderable_Builder>;

} // namespace renderive
