#pragma once

#include "core/script.hpp"
#include "geometry/paddle.hpp"

#include "constants.hpp"
#include "bounce_script.hpp"
#include "player_script.hpp"

#include <optional>


struct wall_script : frog::script
{
    static constexpr float Gravity = 2;

    int element;
    int level;
    bounce_script* bounce = nullptr;

    struct fall
    {
        float time = 0;
        int levels = 1;
    };

    std::optional<fall> falling = std::nullopt;


    wall_script(int element, int level)
        : element(element), level(level) {}

    void init(frog::game_object& object, frog::engine&) override
    {
        using namespace frog;

        bounce = object.get_script<bounce_script>();
    }

    void stable_update(frog::game_object& object, frog::engine& engine) override
    {
        if (falling)
        {
            falling->time += engine.global->stable_frame_time();
            float new_y = level * Wall::Height - falling->time * falling->time * Gravity;

            if (new_y <= (level - falling->levels) * Wall::Height)
            {
                level -= falling->levels;
                new_y = level * Wall::Height;
                falling.reset();
            }

            bounce->set_y(object, new_y);
        }
    }

    void destroyed(frog::game_object&, frog::engine& engine) override
    {
        auto broadcast = [&](auto& wall)
        {
            auto* script = wall.get_script<wall_script>();
            if (script)
            {
                script->wall_destroyed(wall, element, level);
            }
        };

        engine.scenes->current()
            .for_each_object(frog::by_tag("wall"), broadcast);

        engine.scenes->current()
            .for_each_script<player_script>(
                [](auto& pl_scr) { pl_scr.add_score(1); });
    }

    void wall_destroyed(frog::game_object& obj, int el, int lvl)
    {
        // check if the destroyed wall was below us
        if (el == element && lvl < level)
        {
            if (falling)
                falling->levels++;
            else
                falling = { 0.f, 1 };
        }
    }
};
