#ifndef _JFXMAP_TILE_LOADER_H_
#define _JFXMAP_TILE_LOADER_H_

#include <any>
#include <thread>

#include "Singleton.h"

namespace jf {

struct TileIdWrapper {
    TileId tid;
    std::string tileTypeTag;
};

enum class load_task_state { not_inited, inited, finished };

enum class load_task_result { success, cannot_find_tile };

struct load_task {
    template <typename inTileType>
    void init(const TileId &tid) {
        in_tid = {tid, inTileType::tileTypeTag};
        state = load_task::inited;
    };
    bool inited() { return state == load_task_state::inited; }
    bool finished() { return state == load_task_state::finished; }

    TileIdWrapper in_tid;
    // threadSafeSharedPtr<std::any> out_obj;
    std::atomic<load_task_state> state;
    std::stomic<load_tile_result> result;
};

class tile_loader_impl {
   public:
    load_task immediate_task;
    std::condition_variable immediate_tile_cv;
    std::mutex immediate_tile_m;

    tile_loader_impl() {
        std::thread t(&tile_loader_impl::do_loading, this);
        t.detach();
    }
    template <typename TileType>
    std::shared_ptr<TileType> LoadTile(std::string &s, const TileId &intid) {
        // 本函数阻塞执行，所以这个变量在这个函数结束后必然设置为false
        assert(!immediate_task.inited());

        // 即时加载任务初始化
        immediate_task.init<inTileType>(intid);

        std::lock_guard lk(immediate_tile_m);
        immediate_tile_cv.wait_for(!lk, [&immediate_task]() { return immediate_task.finished(); });

        if (immediate_task.result == load_task_result::success) {
            return immediate_task.out_obj;
        }

        return nullptr;
    }

    void do_loading() {
        while (true) {
            // 如果预加载了直接返回
            load_task ret;
            if (jf::Utils::MapValue(preloaded_tiles, intid, ret)) {
                return immediate_task = ret;
            }
        }
    }
    std::shared_ptr<TileType> load_tile(tid) {
        bool succ = sv->LoadTile<inTileType>(tid, s);
        if (!succ) {
            // jf_log_info("[tile_loader_impl::LoadTile] cannot find Tile {0}, {1}", tid.lonId, tid.latId);
            return nullptr;
        }
        std::shared_ptr<TileType> t(new TileType(intid));
        preloaded_tiles.insert(std::make_pair());
        t->Deserialize(s);
        return t;
    }

   private:
    std::map<tileIdWrapper, load_task> preloaded_tiles;
};
typedef Singleton<tile_loader_impl> tile_loader;

}  // namespace jf

#endif