#ifndef _H_RESOURCEMANAGER_H
#define _H_RESOURCEMANAGER_H

#include <memory>
#include <string>
#include <unordered_map>

#include "graphics/background.h"
#include "graphics/shader.h"
#include "graphics/sprite.h"
#include "sound/soundeffect.h"
#include "utility/script.h"
#include "utility/xmlloader.h"
#include <cassert>

class Weapon;
class AnimationSet;
class SpriteComponent;

class ResourceManager {
public:
  ResourceManager() : xmlLoader(std::make_shared<XMLLoader>()) {
    xmlLoader->loadWeapons("weapons");
    xmlLoader->loadAnimations("animations");
    xmlLoader->loadMonsters("monsters");
    xmlLoader->loadTileSets("maps/tilesets");
    xmlLoader->loadMaps("maps");
    xmlLoader->loadScripts("scripts", loadLuaString);
    xmlLoader->loadScenes("scenes");
    xmlLoader->loadSoundEffects("sounds");
    shaders["__fallback__"] = std::make_unique<GenericShader>();
  };

  // Returns a NON-OWNING pointer to a sprite atlas
  SpriteAtlas *loadSpriteAtlas(const std::string &path, float frameSize,
                               bool isDirectional = false);
  // Returns a NON-OWNING pointer to a static sprite
  Sprite *loadSpriteStatic(const std::string &path);
  // Returns a NON-OWNING pointer to a background
  Background *loadBackground(const std::string &path);
  const unsigned loadSoundEffect(const std::string &id);
  template <typename T = Script>
  std::unique_ptr<T> loadScript(const std::string &id);
  std::unique_ptr<Weapon> loadWeapon(const std::string &name,
                                     const unsigned weaponShaderID,
                                     const unsigned bulletShaderID);

  const unsigned loadShader(const std::string &name,
                            const std::string &vertexPath,
                            const std::string &fragPath);
  const SceneData *loadScene(const std::string &id);
  const TileSetData *loadTileSet(const std::string &name);
	const std::vector<AnimationData*>loadAnimationSet(const std::string &id);

  // Returns a NON-OWNING pointer to a shader by ID
  Shader *getShaderByID(unsigned int ID);

  void clearResources();

  ~ResourceManager();

private:
  std::unordered_map<std::string, std::unique_ptr<Shader>> shaders;
  std::unordered_map<unsigned, Shader *> shaderIDs;
  std::unordered_map<std::string, std::unique_ptr<SoundEffect>> sounds;
  std::unordered_map<std::string, std::unique_ptr<Sprite>> sprites;
  std::unordered_map<std::string, std::unique_ptr<Background>> backgrounds;
  std::unordered_map<std::string, std::shared_ptr<TileSetData>> tileSets;

  std::shared_ptr<XMLLoader> xmlLoader;

  static bool loadLuaString(ScriptData *script);
};

template <typename T>
std::unique_ptr<T> ResourceManager::loadScript(const std::string &id) {
  const ScriptData *data = xmlLoader->getScriptData(id);
  if (data == nullptr)
    return nullptr;
  try {
    std::unique_ptr<T> script = std::make_unique<T>(data->script);
    return std::move(script);
  } catch (std::exception &e) {
    LOG(ERROR, "Failed to load script '{}'", data->fileName);
    return nullptr;
  }
}

#endif // _H_RESOURCEMANAGER_H
