#pragma once

#include <cstdint>
#include <cmath>

namespace FreeExile {

/**
 * Deterministic Fixed-Point Integer Math (Q16.16).
 * 16 bits integer, 16 bits fractional.
 * Eliminates cross-platform float nondeterminism between ARM64 (iOS) and x86_64 (Server).
 */
class Fixed32 {
public:
    int32_t raw_value;

    static constexpr int32_t FRACTIONAL_BITS = 16;
    static constexpr int32_t ONE = 1 << FRACTIONAL_BITS;

    constexpr Fixed32() : raw_value(0) {}
    constexpr explicit Fixed32(int32_t raw) : raw_value(raw) {}

    static constexpr Fixed32 from_int(int32_t v) {
        return Fixed32(v << FRACTIONAL_BITS);
    }

    static constexpr Fixed32 from_float(float f) {
        return Fixed32(static_cast<int32_t>(f * ONE));
    }

    float to_float() const {
        return static_cast<float>(raw_value) / static_cast<float>(ONE);
    }

    int32_t to_int() const {
        return raw_value >> FRACTIONAL_BITS;
    }

    Fixed32 operator+(Fixed32 other) const { return Fixed32(raw_value + other.raw_value); }
    Fixed32 operator-(Fixed32 other) const { return Fixed32(raw_value - other.raw_value); }
    Fixed32 operator*(Fixed32 other) const {
        int64_t prod = static_cast<int64_t>(raw_value) * static_cast<int64_t>(other.raw_value);
        return Fixed32(static_cast<int32_t>(prod >> FRACTIONAL_BITS));
    }
    Fixed32 operator/(Fixed32 other) const {
        int64_t num = static_cast<int64_t>(raw_value) << FRACTIONAL_BITS;
        return Fixed32(static_cast<int32_t>(num / other.raw_value));
    }

    bool operator<(Fixed32 other) const { return raw_value < other.raw_value; }
    bool operator>(Fixed32 other) const { return raw_value > other.raw_value; }
    bool operator<=(Fixed32 other) const { return raw_value <= other.raw_value; }
    bool operator>=(Fixed32 other) const { return raw_value >= other.raw_value; }
    bool operator==(Fixed32 other) const { return raw_value == other.raw_value; }
    bool operator!=(Fixed32 other) const { return raw_value != other.raw_value; }
};

struct FixedVector2 {
    Fixed32 x;
    Fixed32 y;

    static FixedVector2 from_floats(float fx, float fy) {
        return { Fixed32::from_float(fx), Fixed32::from_float(fy) };
    }
};

} // namespace FreeExile
