#include "application/lifecycle/RaidContext.h"

#include <atomic>

namespace ta::application::raid_context
{
    namespace
    {
        std::atomic_bool g_raw_in_raid{ false };
        std::atomic_bool g_stable_in_raid{ false };
        std::atomic_bool g_teardown_started{ false };
        std::atomic_bool g_exit_sample_observed{ false };
        std::atomic<float> g_current_zoom{ 1.0f };
    }

    bool raw_in_raid() noexcept
    {
        return g_raw_in_raid.load(std::memory_order_acquire);
    }

    bool stable_in_raid() noexcept
    {
        return g_stable_in_raid.load(std::memory_order_acquire) &&
               !g_teardown_started.load(std::memory_order_acquire);
    }

    bool teardown_in_progress() noexcept
    {
        return g_teardown_started.load(std::memory_order_acquire);
    }

    bool new_raid_candidate() noexcept
    {
        return g_teardown_started.load(std::memory_order_acquire) &&
               g_exit_sample_observed.load(std::memory_order_acquire) &&
               g_raw_in_raid.load(std::memory_order_acquire);
    }

    float current_zoom() noexcept
    {
        return g_current_zoom.load(std::memory_order_relaxed);
    }

    void publish_in_raid_sample(const bool raw_value) noexcept
    {
        g_raw_in_raid.store(raw_value, std::memory_order_release);

        if (g_teardown_started.load(std::memory_order_acquire))
        {
            if (!raw_value)
                g_exit_sample_observed.store(true, std::memory_order_release);

            // A stale true sample can be observed between LocalGame.Stop and
            // the actual InRaid transition. Keep all object callbacks gated
            // until a false sample has occurred and a new live player is
            // confirmed by GameWorld.Update.
            g_stable_in_raid.store(false, std::memory_order_release);
            return;
        }

        g_stable_in_raid.store(raw_value, std::memory_order_release);
    }

    void begin_teardown() noexcept
    {
        bool expected = false;
        if (g_teardown_started.compare_exchange_strong(
                expected, true, std::memory_order_acq_rel, std::memory_order_acquire))
        {
            g_exit_sample_observed.store(false, std::memory_order_release);
        }

        g_stable_in_raid.store(false, std::memory_order_release);
        g_current_zoom.store(1.0f, std::memory_order_relaxed);
    }

    void confirm_new_raid() noexcept
    {
        if (!new_raid_candidate())
            return;

        g_teardown_started.store(false, std::memory_order_release);
        g_exit_sample_observed.store(false, std::memory_order_release);
        g_stable_in_raid.store(true, std::memory_order_release);
    }

    void set_in_raid(const bool value) noexcept
    {
        publish_in_raid_sample(value);
    }

    void set_current_zoom(const float value) noexcept
    {
        g_current_zoom.store(value, std::memory_order_relaxed);
    }

    void reset() noexcept
    {
        g_current_zoom.store(1.0f, std::memory_order_relaxed);
        g_raw_in_raid.store(false, std::memory_order_release);
        g_stable_in_raid.store(false, std::memory_order_release);
        g_exit_sample_observed.store(false, std::memory_order_release);
        g_teardown_started.store(false, std::memory_order_release);
    }
}
