#include "audioplayback.h"
#include "meow.h"
#include "audioutil.h"
#include "sharedstate.h"
#include "audio.h"
#include <cstring>

AudioPlayback::AudioPlayback(MIX_Mixer* mixer) {}

AudioPlayback::AudioPlayback(MIX_Mixer* mixer, AudioSource* source, AudioGroup* group) {
    initialize(mixer, source, group);
}

AudioPlayback::AudioPlayback(MIX_Mixer* mixer, MIX_Audio* audio, AudioGroup* group) {
    initialize(mixer, audio, group);
}

AudioPlayback::~AudioPlayback() {
    deinitilize();
}

bool AudioPlayback::initialize(MIX_Mixer* mixer, AudioSource* source, AudioGroup* group) {
    if (!mixer || !source)
        return false;

    deinitilize();

    p_track = MIX_CreateTrack(mixer);

    if (!p_track)
    {
        ErrorMsg("AudioPlayback: MIX_CreateTrack failed: %s", SDL_GetError());
        return false;
    }

    shState->audio().registerPlayback(this);

    if (group) {
        p_group = group;
        MIX_SetTrackGroup(p_track, group->mixGroup);
        MIX_TagTrack(p_track, group->tag().c_str());
        group->update();
    }

    p_source = source;

    return true;
}

bool AudioPlayback::initialize(MIX_Mixer* mixer, MIX_Audio* audio, AudioGroup* group) {
    return initialize(mixer, new AudioSource(mixer, audio), group);
}

void AudioPlayback::deinitilize() {
    if (p_track)
    {
        shState->audio().unregisterPlayback(this);
        MIX_StopTrack(p_track, 0);
        MIX_DestroyTrack(p_track);
        p_track = nullptr;
    }

    p_source = nullptr;
}

// TODO: Realize panning
bool AudioPlayback::play(int loops, float volume, float pitch, float pan) {/*
    if (!p_track || !p_source)
        return false;
    
    MIX_SetTrackGain(p_track, volume);
    MIX_SetTrackFrequencyRatio(p_track, pitch);
    //MIX_SetTrack3DPosition();
    if (p_group)
        MIX_SetTrackGroup(p_track, p_group->mixGroup);

    return play(loops);*/
    return fadeIn(DEFAULT_FADE_IN, loops, volume, pitch);
}
bool AudioPlayback::play(int loops) {/*
    if (!p_track || !p_source)
        return false;

    MIX_SetTrackLoops(p_track, loops);

    return play();*/
    
    return fadeIn(DEFAULT_FADE_IN, loops);
}

bool AudioPlayback::play() {/*
    if (!p_track || !p_source)
        return false;

    bool result = MIX_SetTrackAudio(p_track, p_source->getAudio());

    if (result)
        result = MIX_PlayTrack(p_track, 0);

    return result;*/

    return fadeIn(DEFAULT_FADE_IN);
}

bool AudioPlayback::stop(){
    if (!p_track)
        return false;
    return MIX_StopTrack(p_track, 0);
}

bool AudioPlayback::isPlaying() const {
    return p_track && MIX_TrackPlaying(p_track);
}

long AudioPlayback::getLength() const {
    if (p_source)
        return MIX_GetAudioDuration(p_source->getAudio());
    return 0.0;
}

double AudioPlayback::getLengthNormalized() const {
    if (p_source)
    {
        MIX_Audio* audio = p_source->getAudio();
        SDL_AudioSpec spec;
        if (MIX_GetAudioFormat(audio, &spec))
            return static_cast<double>(MIX_GetAudioDuration(audio)) / static_cast<double>(spec.freq);
    }
    return 0.0;
}

void AudioPlayback::addTag(std::string tagName) {
    if (!p_track)
        return;
    
    MIX_TagTrack(p_track, tagName.data());
}
void AudioPlayback::removeTag(std::string tagName) {
    if (!p_track)
        return;
    
    MIX_UntagTrack(p_track, tagName.data());
}

void AudioPlayback::clearTags() {
    if (!p_track)
        return;

    int count = 0;
    char** tags = MIX_GetTrackTags(p_track, &count);

    for (int i = 0; i < count; ++i) {
        if (std::strcmp(tags[i], p_group->tag().c_str()) != 0)
            MIX_UntagTrack(p_track, tags[i]);
    }
}

void AudioPlayback::setGroup(int group) {
    AudioGroup* a_group = shState->audio().getGroup(group);
    if (p_track)
        MIX_SetTrackGroup(p_track, a_group->mixGroup);
    p_group = a_group;
    MIX_TagTrack(p_track, a_group->tag().c_str());
    a_group->update();
}

int AudioPlayback::getGroup() const {
    return p_group->id;
}

const std::string& AudioPlayback::getPath() const{
    if (p_source)
        return p_source->getPath();
    return "";
}

MIX_Track* AudioPlayback::getTrack() const {
    return p_track;
}

void AudioPlayback::setLoops(int loops) {
    if (p_track)
        MIX_SetTrackLoops(p_track, loops);
}

int AudioPlayback::getLoops() const {
    if (p_track)
        return MIX_GetTrackLoops(p_track);
    return 0;
}

void AudioPlayback::setVolume(float volume){
    p_volume = volume;
    update();
}

float AudioPlayback::getVolume() const {
    return p_volume;
}

void AudioPlayback::setPitch(float pitch) {
    p_pitch = pitch;
    update();
}

float AudioPlayback::getPitch() const {
    return p_pitch;
}

// TODO: Realize panning
void AudioPlayback::setPanning(float pan) {
    //MIX_SetTrack3DPosition();
}

float AudioPlayback::getPanning() const {
    //if (p_track)
        //return MIX_GetTrack3DPosition();
    return 0;
}

void AudioPlayback::setPlaybackPosition(double time) {
    if (p_track)
        MIX_SetTrackPlaybackPosition(p_track, MIX_TrackMSToFrames(p_track, time * 1000.0));
}

double AudioPlayback::getPlaybackPosition() const {
    if (!p_track)
        return 0;
    return MIX_TrackFramesToMS(p_track, MIX_GetTrackPlaybackPosition(p_track)) / 1000.0;
}

bool AudioPlayback::fadeIn(double time, int loops, float volume, float pitch, float pan) {
    if (!p_track || !p_source)
        return false;
    
    p_volume = volume;
    p_pitch = pitch;
    update();
    //MIX_SetTrack3DPosition();
    if (p_group)
        MIX_SetTrackGroup(p_track, p_group->mixGroup);

    return fadeIn(time, loops);
}

bool AudioPlayback::fadeIn(double time, int loops) {
    if (!p_track || !p_source)
        return false;

    bool result = MIX_SetTrackAudio(p_track, p_source->getAudio());

    SDL_PropertiesID props = SDL_CreateProperties();
    SDL_SetNumberProperty(props, MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER, time * 1000.0);
    SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, loops);
    if (result)
    {
        result = MIX_PlayTrack(p_track, props);
    }
    SDL_DestroyProperties(props);

    return result;
}

bool AudioPlayback::fadeIn(double time) {
    if (!p_track || !p_source)
        return false;

    bool result = MIX_SetTrackAudio(p_track, p_source->getAudio());

    SDL_PropertiesID props = SDL_CreateProperties();
    SDL_SetNumberProperty(props, MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER, time * 1000.0);
    SDL_SetNumberProperty(props, MIX_PROP_PLAY_START_FRAME_NUMBER, startSample);
    SDL_SetNumberProperty(props, MIX_PROP_PLAY_MAX_FRAME_NUMBER, maxSample);
    if (result)
    {
        result = MIX_PlayTrack(p_track, props);
    }
    SDL_DestroyProperties(props);

    return result;
}

void AudioPlayback::fadeOut(double time) {
    if (isPlaying())
        MIX_StopTrack(p_track, MIX_TrackMSToFrames(p_track, time * 1000.0));
}

void AudioPlayback::update() {
    if (p_track) {
        if (p_group) {
            MIX_SetTrackGain(p_track, p_volume * p_group->getVolume());
            MIX_SetTrackFrequencyRatio(p_track, p_pitch * p_group->getPitch());
        }
        else {
            MIX_SetTrackGain(p_track, p_volume);
            MIX_SetTrackFrequencyRatio(p_track, p_pitch);
        }
    }
}