// SPDX-License-Identifier: Unlicense

#include "shred.h"

#ifdef SHR3D_SHRED

#include "file.h"
#include "global.h"
#include "sfxCore/neuralAmpModeler/sfxCoreNeuralAmpModeler.h"
#include "miniz.h"

Shred::Info Shred::parse(const u8* shredData, u64 shredDataSize)
{
  Shred::Info shredInfo;

  mz_zip_archive zip_archive{};
  {
    const mz_bool status = mz_zip_reader_init_mem(&zip_archive, shredData, shredDataSize, 0);
    ASSERT(status == 1);
  }

  const u32 fileCount = mz_zip_reader_get_num_files(&zip_archive);
  for (u32 i = 0; i < fileCount; ++i)
  {
    std::string filename;
    {
      const u32 size = mz_zip_reader_get_filename(&zip_archive, i, nullptr, 0);
      filename.resize(size - 1);
      mz_zip_reader_get_filename(&zip_archive, i, reinterpret_cast<char*>(filename.data()), size);
    }
    if (filename == "song.ini")
    {
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      std::string songIni;
      songIni.resize(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, songIni.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);

      shredInfo.songIni = Ini::loadIniContent(reinterpret_cast<const char*>(songIni.c_str()), file_stat.m_uncomp_size);
    }
    else if (filename == "album.png")
    {
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      shredInfo.albumPng.resize(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, shredInfo.albumPng.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);
    }
    else if (filename == "song.ogg")
    {
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      shredInfo.songOgg.resize(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, shredInfo.songOgg.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);
    }
    else if (filename == "preview.ogg")
    {
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      shredInfo.previewOgg.resize(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, shredInfo.previewOgg.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);
    }
    else if (filename == "lyrics.ini")
    {
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      std::string lyricsIni;
      lyricsIni.resize(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, lyricsIni.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);

      shredInfo.lyricsIni = Ini::loadIniContent_keepKeyOrder(reinterpret_cast<const char*>(lyricsIni.c_str()), file_stat.m_uncomp_size);
    }
    else if (filename == "tones.ini")
    {
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      std::string tonesIni;
      tonesIni.resize(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, tonesIni.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);

      shredInfo.tonesIni = Ini::loadIniContent_keepKeyOrder(tonesIni.c_str(), file_stat.m_uncomp_size);
    }
    else if (File::extension(filename.c_str()) == std::string(".ini"))
    {
      std::map<std::string, std::vector<Ini::KeyValue>>& arrangementIni = shredInfo.arrangements[filename.substr(0, filename.size() - 4)];

      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      std::string arrangementIniStr;
      arrangementIniStr.resize(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, arrangementIniStr.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);

      arrangementIni = Ini::loadIniContent_keepKeyOrder(reinterpret_cast<const char*>(arrangementIniStr.c_str()), file_stat.m_uncomp_size);
    }
    else if (File::extension(filename.c_str()) == std::string(".nam"))
    {
#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      std::vector<u8> data(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, data.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);
      const std::string cacheFilename = Settings::pathCache + filename;
#ifdef PLATFORM_EMSCRIPTEN
      if (Global::namFileCache.size() == 0)
        Global::namFileCache.reserve(100);
      Global::namFileCache[cacheFilename] = std::move(data);
#else // PLATFORM_EMSCRIPTEN
      File::write(cacheFilename.c_str(), data.data(), data.size());
#endif // PLATFORM_EMSCRIPTEN
      Global::NeuralAmpModeler_NamFiles.push_back(cacheFilename);
#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
    }
    else if (File::extension(filename.c_str()) == std::string(".wav"))
    {
#ifdef SHR3D_SFX_CORE_NEURALAMPMODELER
      mz_zip_archive_file_stat file_stat;
      mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
      std::vector<u8> data(file_stat.m_uncomp_size);
      const mz_bool status = mz_zip_reader_extract_to_mem(&zip_archive, i, data.data(), file_stat.m_uncomp_size, 0);
      ASSERT(status == 1);
      const std::string cacheFilename = Settings::pathCache + filename;

#ifdef PLATFORM_EMSCRIPTEN
      Global::namFileCache[cacheFilename] = std::move(data);
#else // PLATFORM_EMSCRIPTEN
      File::write(cacheFilename.c_str(), data.data(), data.size());
#endif // PLATFORM_EMSCRIPTEN

      Global::NeuralAmpModeler_IrFiles.push_back(cacheFilename);
#endif // SHR3D_SFX_CORE_NEURALAMPMODELER
    }
    else
    {
      ASSERT(false);
    }
  }

  ASSERT(!shredInfo.songIni.empty()); // missing song.ini?

  return shredInfo;
}

#endif // SHR3D_SHRED
