#include <object/sound.hpp>

namespace Game::Object {
    Sound::Sound(std::string path, Format format, int volume)
        : mAudioBuffer(nullptr), mAudioLength(0), mAudioStream(nullptr), mSourceSpec{}, mVolume(volume) {
        if (format == Format::WAV) {
            if (!SDL_LoadWAV(path.c_str(), &mSourceSpec, &mAudioBuffer, &mAudioLength)) {
                ERROR("Failed to load WAV file: " << SDL_GetError());
                return;
            }

            LOG("WAV file loaded successfully");

            mAudioStream = SDL_OpenAudioDeviceStream(
                Audio::Audio::getInstance().getAudioDevice(), &mSourceSpec, nullptr, nullptr
            );
            if (!mAudioStream) {
                ERROR("Failed to create audio stream: " << SDL_GetError());
                SDL_free(mAudioBuffer);
                mAudioBuffer = nullptr;
                mAudioLength = 0;
                mSourceSpec = {};
            }
        } else {
            ERROR("Unsupported audio format");
        }
    }

    Sound::Sound(const Sound& other)
        : mAudioBuffer(nullptr), mAudioLength(0), mAudioStream(nullptr), mSourceSpec(other.mSourceSpec), mVolume(other.mVolume) {
        if (&other == this) return;

        if (other.mAudioBuffer && other.mAudioLength > 0) {
            Uint8* newBuffer = static_cast<Uint8*>(SDL_malloc(other.mAudioLength));
            if (!newBuffer) {
                ERROR("Failed to allocate memory for audio buffer copy");
                return;
            }
            SDL_memcpy(newBuffer, other.mAudioBuffer, other.mAudioLength);
            mAudioBuffer = newBuffer;
            mAudioLength = other.mAudioLength;
        }

        mAudioStream = SDL_OpenAudioDeviceStream(
            Audio::Audio::getInstance().getAudioDevice(), &mSourceSpec, nullptr, nullptr
        );
        if (!mAudioStream) {
            ERROR("Failed to create audio stream for copied Sound: " << SDL_GetError());
        }
    }

    Sound& Sound::operator=(const Sound& other) {
        if (&other == this) return *this;

        Uint8* newBuffer = nullptr;
        Uint32 newLength = 0;

        if (other.mAudioBuffer && other.mAudioLength > 0) {
            newBuffer = static_cast<Uint8*>(SDL_malloc(other.mAudioLength));
            if (!newBuffer) {
                ERROR("Failed to allocate memory for audio buffer copy");
                return *this;
            }
            SDL_memcpy(newBuffer, other.mAudioBuffer, other.mAudioLength);
            newLength = other.mAudioLength;
        }

        SDL_AudioStream* newStream = SDL_OpenAudioDeviceStream(
            Audio::Audio::getInstance().getAudioDevice(), &other.mSourceSpec, nullptr, nullptr
        );
        if (!newStream) {
            ERROR("Failed to create audio stream for copied Sound: " << SDL_GetError());
            if (newBuffer) SDL_free(newBuffer);
            return *this;
        }

        if (mAudioBuffer) SDL_free(mAudioBuffer);
        if (mAudioStream && SDL_WasInit(SDL_INIT_AUDIO)) SDL_DestroyAudioStream(mAudioStream);

        mAudioBuffer = newBuffer;
        mAudioLength = newLength;
        mAudioStream = newStream;
        mSourceSpec = other.mSourceSpec;
        mVolume = other.mVolume;

        return *this;
    }

        Sound::Sound(Sound&& other)
                : mAudioBuffer(other.mAudioBuffer),
                    mAudioLength(other.mAudioLength),
                    mAudioStream(other.mAudioStream),
                    mSourceSpec(other.mSourceSpec),
                    mVolume(other.mVolume) {
                other.mAudioBuffer = nullptr;
                other.mAudioLength = 0;
                other.mAudioStream = nullptr;
                other.mSourceSpec = {};
        }

    Sound& Sound::operator=(Sound&& other) {
        if (this != &other) {
            if (mAudioBuffer) SDL_free(mAudioBuffer);
            if (mAudioStream && SDL_WasInit(SDL_INIT_AUDIO)) SDL_DestroyAudioStream(mAudioStream);

            mAudioBuffer = other.mAudioBuffer;
            mAudioLength = other.mAudioLength;
            mAudioStream = other.mAudioStream;
            mSourceSpec = other.mSourceSpec;
            mVolume = other.mVolume;

            other.mAudioBuffer = nullptr;
            other.mAudioLength = 0;
            other.mAudioStream = nullptr;
            other.mSourceSpec = {};
        }
        return *this;
    }

    Sound::~Sound() {
        if (mAudioBuffer) {
            SDL_free(mAudioBuffer);
            mAudioBuffer = nullptr;
        }
        if (mAudioStream) {
            if (SDL_WasInit(SDL_INIT_AUDIO)) {
                SDL_DestroyAudioStream(mAudioStream);
            }
            mAudioStream = nullptr;
        }

        LOG("Destroyed Sound");
    }

    void Sound::play() {
        if (mAudioStream && mAudioBuffer && mAudioLength > 0) {
            SDL_ClearAudioStream(mAudioStream);

            if (!SDL_PutAudioStreamData(mAudioStream, mAudioBuffer, mAudioLength)) {
                ERROR("Failed to queue audio data: " << SDL_GetError());
                return;
            }

            if (!SDL_FlushAudioStream(mAudioStream)) {
                ERROR("Failed to flush audio stream: " << SDL_GetError());
                return;
            }

            if (!SDL_ResumeAudioStreamDevice(mAudioStream)) {
                ERROR("Failed to resume audio stream device: " << SDL_GetError());
            }
        }
    }
}