#pragma once

#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/resource/binary_reader.h>
#include <frostbite2D/resource/asset.h>
#include <optional>
#include <string>
#include <map>
#include <vector>
#include <list>
#include <memory>

namespace frostbite2D {

struct AudioRef {
  std::string path;
  std::string npkFile;
  uint32 offset = 0;
  uint32 size = 0;
  bool loaded = false;
};

struct CachedAudioData {
  std::vector<uint8> data;
  uint64 lastUseTime = 0;
  size_t memoryUsage = 0;
};

class SoundPackArchive {
public:
  static SoundPackArchive& get();

  SoundPackArchive(const SoundPackArchive&) = delete;
  SoundPackArchive& operator=(const SoundPackArchive&) = delete;
  SoundPackArchive(SoundPackArchive&&) = delete;
  SoundPackArchive& operator=(SoundPackArchive&&) = delete;

  void setSoundPackDirectory(const std::string& dir);
  const std::string& getSoundPackDirectory() const;

  void init();
  void close();
  bool isOpen() const;

  bool hasAudio(const std::string& path) const;
  std::optional<AudioRef> getAudio(const std::string& path);
  std::vector<std::string> listAudios() const;

  std::optional<std::vector<uint8>> getAudioData(const AudioRef& audio);

  void setCacheSize(size_t maxBytes);
  void clearCache();
  size_t getCacheUsage() const;

  void setDefaultAudio(const std::string& audioPath);
  const std::string& getDefaultAudioPath() const;

private:
  SoundPackArchive() = default;
  ~SoundPackArchive() = default;

  std::string normalizePath(const std::string& path) const;
  void scanNpkFiles();
  bool parseNpkFile(const std::string& npkPath);
  bool loadAudioData(AudioRef& audio);
  void evictCacheIfNeeded(size_t requiredSize);
  void updateCacheUsage(const std::string& audioPath);
  std::string readNpkInfoString(BinaryReader& reader);

  static const uint8 NPK_KEY[256];

  std::string soundPackDirectory_ = "SoundPacks";
  bool initialized_ = false;
  std::map<std::string, AudioRef> audioIndex_;
  std::map<std::string, CachedAudioData> audioCache_;
  std::list<std::string> lruList_;
  size_t maxCacheSize_ = 256 * 1024 * 1024;
  size_t currentCacheSize_ = 0;
  std::string defaultAudioPath_;
};

}
