#include "game/scenes/timeline.hpp"

#include <algorithm>

namespace Scenes {

    auto Timeline::step(int duration_ms, StepFn fn) -> Timeline& {
        Step s;
        s.duration_ms = duration_ms;
        s.continuous = std::move(fn);
        steps_.push_back(std::move(s));
        return *this;
    }

    auto Timeline::once(OnceFn fn) -> Timeline& {
        Step s;
        s.duration_ms = 0;
        s.oneshot = std::move(fn);
        steps_.push_back(std::move(s));
        return *this;
    }

    void Timeline::flushOneShots() {
        while (current_ < steps_.size() && steps_[current_].duration_ms == 0) {
            auto& s = steps_[current_];
            if (!s.entered) {
                s.entered = true;
                if (s.oneshot) {
                    s.oneshot();
                }
            }
            ++current_;
            elapsed_in_step_ = 0;
        }
    }

    void Timeline::tick(int delta_ms) {
        if (skipped_) {
            return;
        }
        flushOneShots();
        if (current_ >= steps_.size()) {
            return;
        }

        auto& s = steps_[current_];
        if (!s.entered) {
            s.entered = true;
            // Primer tick dins del pas: cridem amb progress=0 si hi ha callback.
            if (s.continuous) {
                s.continuous(0.0F);
            }
        }

        elapsed_in_step_ += delta_ms;
        if (elapsed_in_step_ >= s.duration_ms) {
            // Tancament del pas: una crida final amb progress=1.
            if (s.continuous) {
                s.continuous(1.0F);
            }
            ++current_;
            elapsed_in_step_ = 0;
            // Pot ser que el següent pas siga una cadena de one-shots.
            flushOneShots();
        } else if (s.continuous) {
            const float P = static_cast<float>(elapsed_in_step_) /
                static_cast<float>(std::max(1, s.duration_ms));
            s.continuous(P);
        }
    }

    void Timeline::skip() {
        skipped_ = true;
        current_ = steps_.size();
    }

    void Timeline::reset() {
        for (auto& s : steps_) {
            s.entered = false;
        }
        current_ = 0;
        elapsed_in_step_ = 0;
        skipped_ = false;
    }

    auto Timeline::done() const -> bool {
        return skipped_ || current_ >= steps_.size();
    }

    auto Timeline::currentProgress() const -> float {
        if (current_ >= steps_.size()) {
            return 1.0F;
        }
        const auto& s = steps_[current_];
        if (s.duration_ms <= 0) {
            return 0.0F;
        }
        return static_cast<float>(elapsed_in_step_) / static_cast<float>(s.duration_ms);
    }

}  // namespace Scenes
