
#include "scene.hh"

#include <fstream>

#include "itty/paths.hh"
#include "itty/vec.hh"
#include "itty/json.hh"
#include "itty/input.hh"

//////////////////////////////////////////////////////////////////////////////
/// Level loading
//////////////////////////////////////////////////////////////////////////////

struct Loader {
        Scene& sc;

        std::filesystem::path cwd;
        std::filesystem::path music;
        std::string title;
        size_t width;
        size_t height;
        size_t tilewidth;
        size_t tileheight;
        std::vector<char> ts_types;

        Loader(Scene& sc) : sc{sc} {}

        void operator()(const std::filesystem::path& path);
        void parse(const itty::Json_obj& o);
        void parse_property(const itty::Json_obj& o);
        void parse_tileset(const itty::Json_obj& o);
        void parse_tileset_tile(const itty::Json_obj& o);
        void parse_layer(const itty::Json_obj& o);
        void parse_tilelayer(const itty::Json_obj& o);
        void parse_objectgroup(const itty::Json_obj& o);
        void parse_object(const itty::Json_obj& o);
        void load_background_music();
};

void Loader::operator()(const std::filesystem::path& path)
{
        cwd = path.parent_path();

        if (std::ifstream i{path}) {
                itty::Json_doc doc{itty::slurp(i)};
                parse(doc.root().obj());
        }
        else throw std::runtime_error{"can't open \"" + path.string() + "\""};

        SDL_Log("Playing %s", title.data());
        load_background_music();
}

void Loader::parse(const itty::Json_obj& o)
{
        width = o.at("width").num_as<long>();
        height = o.at("height").num_as<long>();
        tilewidth = o.at("tilewidth").num_as<long>();
        tileheight = o.at("tileheight").num_as<long>();

        for (auto& p : o.at("properties").arr())
                parse_property(p.obj());

        for (auto& p : o.at("tilesets").arr())
                parse_tileset(p.obj());

        for (auto& p : o.at("layers").arr())
                parse_layer(p.obj());
}

void Loader::parse_property(const itty::Json_obj& o)
{
        if (o.at("name").str() == "music")
                music = cwd / itty::escape_json_string(o.at("value").str());
        else if (o.at("name").str() == "title")
                title = o.at("value").str();
}

void Loader::parse_tileset(const itty::Json_obj& o)
{
        ts_types.resize(o.at("tilecount").num_as<long>());

        sc.bkgr.add_tiles(cwd / itty::escape_json_string(o.at("image").str()),
                        o.at("imagewidth").num_as<long>() / tilewidth,
                        o.at("imageheight").num_as<long>() / tileheight);

        for (auto& t : o.at("tiles").arr())
                parse_tileset_tile(t.obj());
}

void Loader::parse_tileset_tile(const itty::Json_obj& o)
{
        ts_types[o.at("id").num_as<long>()] = o.at("type").str() == "solid";
}

void Loader::parse_layer(const itty::Json_obj& o)
{
        if (o.at("type").str() == "tilelayer")
                parse_tilelayer(o);
        else if (o.at("type").str() == "objectgroup")
                parse_objectgroup(o);
}

void Loader::parse_tilelayer(const itty::Json_obj& o)
{
        sc.bkgr.resize(width, height);
        sc.phys.resize(width, height);
        auto i = sc.bkgr.begin();
        auto j = sc.phys.begin();

        for (auto& d : o.at("data").arr()) {
                *i = d.num_as<long>();
                *j = *i ? ts_types[(*i - 1) & 0xffff] : 0;
                ++i;
                ++j;
        }
}

void Loader::parse_objectgroup(const itty::Json_obj& o)
{
        for (auto& o : o.at("objects").arr())
                parse_object(o.obj());
}

void Loader::parse_object(const itty::Json_obj& o)
{
        auto type = o.at("type").str();
        auto name = o.at("name").str();
        auto box = itty::Box::from_corner(itty::Vec(o.at("x").num(), o.at("y").num()),
                        itty::Vec(o.at("width").num(), o.at("height").num()));
        if (type == "player") sc.mobs.construct("player", box);
        else if (type == "room") sc.rooms.push_back(box);
        else sc.places.emplace_back(std::string(type), std::string(name), box);
}

void Loader::load_background_music()
{
        sc.music = MIX_CreateTrack(itty::mixer);
        auto a = itty::load_audio(music, false);
        MIX_SetTrackAudio(sc.music, a);
        MIX_DestroyAudio(a);
        auto p = SDL_CreateProperties();
        SDL_SetNumberProperty(p, MIX_PROP_PLAY_LOOPS_NUMBER, -1);
        MIX_PlayTrack(sc.music, p);
        SDL_DestroyProperties(p);
}

void Scene::load(const std::filesystem::path& path)
{
        itty::Prof_lock _("Loading scene");
        Loader{*this}(path);
}

