/****************************************************************************
 ** ScStw Libraries
 ** Copyright (C) 2020  Itsblue development
 **
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License as published by
 ** the Free Software Foundation, either version 3 of the License, or
 ** (at your option) any later version.
 **
 ** This program is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details.
 **
 ** You should have received a copy of the GNU General Public License
 ** along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************/

#include "../headers/scstwsoundplayer.h"

ScStwSoundPlayer::ScStwSoundPlayer(QObject *parent) : QObject(parent)
{
    this->waitLoop = new QEventLoop(this);
    this->waitTimer = new QTimer(this);

    this->soundFiles.insert(AtYourMarks, {{"path","qrc:/sound/AtYourMarksSound.wav"}, {"duration", 1000}});
    this->soundFiles.insert(Ready, {{"path","qrc:/sound/ReadySound.wav"}, {"duration", 570}});
    this->soundFiles.insert(Start, {{"path","qrc:/sound/StartsignalSoundExtended.wav"}, {"duration", 3200}});
    this->soundFiles.insert(FalseStart, {{"path","qrc:/sound/FalseStartSound.wav"}, {"duration", 2000}});

    this->soundEffect = new QSoundEffect(this);
    this->soundEffect->setLoopCount(1);

    connect(this, &ScStwSoundPlayer::playbackStarted, this->waitLoop, &QEventLoop::quit);
    connect(this->soundEffect, &QSoundEffect::playingChanged, this->waitLoop, &QEventLoop::quit);
}

ScStwSoundPlayer::PlayResult ScStwSoundPlayer::play(ScStwSoundPlayer::StartSound sound, double volume, double *timeOfStart) {
    if(!this->soundFiles.contains(sound)) {
        qDebug() << "[ERROR][SoundPlayer] Sound file was not found for sound" << sound;
        return ScStwSoundPlayer::Error;
    }

    // stop playback
    if(this->soundEffect->isPlaying())
        this->soundEffect->stop();

    // update currently playing action
    this->currentlyPlayingSound = sound;

    // update volume

#ifdef RASPI
    // set volume on raspi using alsamixer
    QProcess::execute("amixer -q -M sset PCM " + QString::number(volume * 100, 'f', 0) + "%");
#else
    this->soundEffect->setVolume(volume);
#endif

    // load
    this->soundEffect->setSource(this->soundFiles[sound]["path"].toString());

    // wait for the effect to load
    QEventLoop loop;
    while(this->soundEffect->status() != QSoundEffect::Ready) {
        qDebug() << "[DEBUG][Sound] Sound is not ready yet, status is: " << this->soundEffect->status();
        QObject::connect(this->soundEffect, &QSoundEffect::statusChanged, &loop, &QEventLoop::quit);
        loop.exec();
    }

    qDebug() << "[DEBUG][Sound] Playing sound now: " << sound;

    // start
    this->soundEffect->play();

    // emit a playback start
    emit this->playbackStarted();

    // save started at
    this->playingStartedAt = QDateTime::currentMSecsSinceEpoch();

    // pass the time of start if requested
    if(timeOfStart != nullptr) {
        *timeOfStart = this->playingStartedAt;
    }

    if(sound < Start)
        return this->waitForSoundFinish();

    return ScStwSoundPlayer::Success;
}

bool ScStwSoundPlayer::cancel() {
    if(!this->soundEffect->isPlaying() )
        return false;

    // stop playback
    this->soundEffect->stop();
    this->waitLoop->exit(-1);

    return true;
}

ScStwSoundPlayer::PlayResult ScStwSoundPlayer::waitForSoundFinish(double *timeOfStop) {
    if(!this->soundEffect->isPlaying())
        return ScStwSoundPlayer::Error;

    // wait until the audio output reports the sound is over
    if(waitLoop->exec() == -1)
        return ScStwSoundPlayer::Cancelled;

    // wait until the sound is actually over
    // the timeOffset is the buffer time before the audio started!
    int timeOffset = this->soundFiles[this->currentlyPlayingSound]["duration"].toDouble() - (QDateTime::currentMSecsSinceEpoch() - playingStartedAt);

    if(timeOffset > 0) {
        QTimer timer;
        timer.singleShot(timeOffset, this->waitLoop, &QEventLoop::quit);
        this->waitLoop->exec();
    }

    // calculate the point in time where the sound playback actually ended
    if(timeOfStop != nullptr) {
        int latency = this->playingStartedAt + this->soundFiles[this->currentlyPlayingSound]["duration"].toDouble();
        *timeOfStop = QDateTime::currentMSecsSinceEpoch() - latency;
    }

    return ScStwSoundPlayer::Success;
}

bool ScStwSoundPlayer::isPlaying() {
    return this->soundEffect->isPlaying();
}
