#include <frostbite2D/audio/sound.h>
#include <frostbite2D/audio/audio_system.h>
#include <frostbite2D/resource/asset.h>
#include <frostbite2D/resource/sound_pack_archive.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include <algorithm>

namespace frostbite2D {

Ptr<Sound> Sound::loadFromFile(const std::string& path) {
  auto& asset = Asset::get();
  std::string fullPath = asset.resolvePath(path);
  
  Mix_Chunk* chunk = Mix_LoadWAV(fullPath.c_str());
  if (!chunk) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load sound from file %s: %s", fullPath.c_str(), Mix_GetError());
    return nullptr;
  }

  return Ptr<Sound>(new Sound(chunk, path));
}

Ptr<Sound> Sound::loadFromMemory(const uint8* data, size_t size) {
  SDL_RWops* rw = SDL_RWFromConstMem(data, static_cast<int>(size));
  if (!rw) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create RWops from memory: %s", SDL_GetError());
    return nullptr;
  }

  Mix_Chunk* chunk = Mix_LoadWAV_RW(rw, 1);
  if (!chunk) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load sound from memory: %s", Mix_GetError());
    return nullptr;
  }

  return Ptr<Sound>(new Sound(chunk));
}

Ptr<Sound> Sound::loadFromNpk(const std::string& audioPath) {
  SoundPackArchive& archive = SoundPackArchive::get();
  
  if (!archive.isOpen()) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SoundPackArchive not initialized");
    return nullptr;
  }

  auto audioOpt = archive.getAudio(audioPath);
  if (!audioOpt) {
    std::string defaultPath = archive.getDefaultAudioPath();
    if (!defaultPath.empty()) {
      audioOpt = archive.getAudio(defaultPath);
    }
    if (!audioOpt) {
      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio not found in NPK: %s", audioPath.c_str());
      return nullptr;
    }
  }

  auto dataOpt = archive.getAudioData(*audioOpt);
  if (!dataOpt || dataOpt->empty()) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get audio data for: %s", audioPath.c_str());
    return nullptr;
  }

  return loadFromMemory(dataOpt->data(), dataOpt->size());
}

Sound::Sound(Mix_Chunk* chunk, const std::string& path)
  : chunk_(chunk), path_(path) {
}

Sound::~Sound() {
  if (chunk_) {
    Mix_FreeChunk(chunk_);
    chunk_ = nullptr;
  }
}

int Sound::play(int loops, int channel) {
  if (!chunk_) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Sound chunk is null");
    return -1;
  }

  if (!AudioSystem::get().isInitialized()) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio system not initialized");
    return -1;
  }

  int vol = static_cast<int>(volume_ * AudioSystem::get().getSoundVolume() * AudioSystem::get().getMasterVolume() * MIX_MAX_VOLUME);
  Mix_VolumeChunk(chunk_, vol);

  int result = Mix_PlayChannel(channel, chunk_, loops);
  if (result == -1) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to play sound: %s", Mix_GetError());
  } else {
    lastChannel_ = result;
  }
  return result;
}

void Sound::setVolume(float volume) {
  volume_ = std::clamp(volume, 0.0f, 1.0f);
}

float Sound::getVolume() const {
  return volume_;
}

int Sound::getLastChannel() const {
  return lastChannel_;
}

int Sound::playLoop(int channel) {
  return play(-1, channel);
}

void Sound::stop(int channel) {
  if (channel >= 0) {
    Mix_HaltChannel(channel);
  }
}

void Sound::stopAll() {
  Mix_HaltChannel(-1);
}

bool Sound::isPlaying(int channel) {
  if (channel < 0) {
    return false;
  }
  return Mix_Playing(channel) == 1;
}

void Sound::pause(int channel) {
  if (channel >= 0) {
    Mix_Pause(channel);
  }
}

void Sound::resume(int channel) {
  if (channel >= 0) {
    Mix_Resume(channel);
  }
}

void Sound::pauseAll() {
  Mix_Pause(-1);
}

void Sound::resumeAll() {
  Mix_Resume(-1);
}

}
