
#include "mob_main.hh"

#include <algorithm>

#include "itty/paths.hh"

#include "mob_solid.hh"
#include "mob_hostile.hh"
#include "mob_effect.hh"
#include "mob_player.hh"
#include "strings.hh"
#include "scene.hh"

Mobs::Mobs()
{
        next_tick = SDL_GetTicks() + 33;
        frame = 0;
}

Mobs::~Mobs()
{
        atlas.save_png(itty::paths.state.w(basedir / "mobs.png"));
        atlas.clear();
        quads.clear();
        frames.clear();
        audios.clear();
}

void Mobs::clear()
{
        mobs.remove_if([this](auto& m){ return m.get() != pc; });
}

void Mobs::update(Scene& sc)
{
        if ((tick = (next_tick < SDL_GetTicks()))) {
                next_tick += 33;
                frame++;
        }

        auto fun = [&sc](std::unique_ptr<Mob>& m)
        {
                if (m->is(Mob::ROOM) && !sc.room.overlaps(m->box))
                        return true;
                if (m->update(sc))
                        return true;
                if (m->is(Mob::HARM))
                        sc.mobs.pc->harm(m->box);
                if (m->is(Mob::PHYS))
                        sc.phys.add_body(m.get());
                return false;
        };

        mobs.remove_if(fun);
}

void Mobs::render(const itty::Box& view)
{
        quads.clear();
        for (auto& m : mobs)
                m->render(*this);
        quads.upload();
        quads.render(view);
}

Mob* Mobs::construct(const std::string& type, const itty::Box& box, Place* place)
{
        Mob* m = type == "bone" ? (Mob*)new Mob_bone(*this, box, place)
                : type == "bones" ? (Mob*)new Mob_bones(*this, box, place)
                : type == "crack" ? (Mob*)new Mob_crack(*this, box, place)
                : type == "crawler" ? (Mob*)new Mob_crawler(*this, box, place)
                : type == "dust" ? (Mob*)new Mob_dust(*this, box, place)
                : type == "mirror" ? (Mob*)new Mob_mirror(*this, box, place)
                : type == "thorns" ? (Mob*)new Mob_thorns(*this, box, place)
                : type == "peeper" ? (Mob*)new Mob_peeper(*this, box, place)
                : type == "player" ? (Mob*)new Mob_player(*this, box, place)
                : type == "poof" ? (Mob*)new Mob_poof(*this, box, place)
                : type == "rotten" ? (Mob*)new Mob_rotten(*this, box, place)
                : type == "splat" ? (Mob*)new Mob_splat(*this, box, place)
                : (Mob*)nullptr;

        if (m == nullptr) {
                log_warn("Unrecognized mob type: %s", type.c_str());
                return nullptr;
        }
        mobs.emplace_back(m);
        return m;
}

Mob* Mobs::construct(const std::string& type, const itty::Vec& p, const itty::Vec& r, Place* place)
{
        return construct(type, itty::Box::from_center(p, r), place);
}

ssize_t Mobs::load_frames(const std::string& name, int w, int h)
{
        auto i = frames.find(name);
        if (i == frames.end()) {
                auto path = itty::paths.data.r(basedir / "texture" / name);
                i = frames.emplace(name, atlas.load(path, w, h)).first;
        }
        return i->second;
}

MIX_Audio* Mobs::load_audio(const std::string& name)
{
        auto i = audios.find(name);
        if (i == audios.end()) {
                auto path = itty::paths.data.r(basedir / "sound" / name);
                i = audios.emplace(name, Audio_ptr(itty::load_audio(path, true))).first;
        }
        return i->second.get();
}

