#include <QObject>
#include <QTime>
#include <QSettings>
#include <QSound>

#include "boxingtimer.h"
#include "boxingsettings.h"

BoxingTimer::BoxingTimer() :
    mRound(0),
    mInnerRemainingMilliseconds(0),
    mStatus(Status::Stop),
    mOldStatus(Status::Default) {
    this->setTimerType(Qt::VeryCoarseTimer);

    this->settings = new BoxingSettings();
    this->mRemainingMilliseconds = this->settings->getRoundMilliseconds();

    this->setInnerTimers();

    this->playBell = new QSound(":/audio/resources/play.wav");
    this->restBell = new QSound(":/audio/resources/rest.wav");
    this->stopBell = new QSound(":/audio/resources/stop.wav");
    this->innerBell = new QSound(":/audio/resources/inner.wav");

    QObject::connect(this, SIGNAL(statusChanged()), this, SLOT(applyStatus()));
    QObject::connect(this, SIGNAL(timeout()), this, SLOT(updateRemainingMilliseconds()));
}

const QString BoxingTimer::getPreset() {
    return this->settings->getPresetName();
}

void BoxingTimer::setRound(const int &round) {
    this->mRound = round;

    emit roundsToStringChanged();
}

void BoxingTimer::setRemainingMilliseconds(const int &milliseconds) {
    this->mRemainingMilliseconds = milliseconds;

    emit remainingTimeToStringChanged();
}

void BoxingTimer::setStatus(const Status &status) {
    if (this->mStatus == Status::Start || this->mStatus == Status::Rest) {
        this->mOldStatus = this->mStatus;
    }

    this->mStatus = status;

    emit statusChanged();
}

void BoxingTimer::stop() {
    QTimer::stop();

    emit activeChanged();
}

void BoxingTimer::start(const int &milliseconds) {
    QTimer::start(milliseconds);

    emit activeChanged();
}

void BoxingTimer::applyStatus() {
    this->songsBell();

    switch (this->mStatus) {
        case Status::Reset:
            if (this->mOldStatus == Status::Start) {
                this->setRemainingMilliseconds(this->settings->getRoundMilliseconds());
                this->setInnerTimers();
            } else if (this->mOldStatus == Status::Rest) {
                this->setRemainingMilliseconds(this->settings->getRestMilliseconds());
                this->setInnerTimers();
            }

            if (this->isActive()) {
                this->setStatus(this->mOldStatus);
            }

            break;
        case Status::Start:
        case Status::Rest:
            this->start(1000);

            if (this->mRound == 0) {
                this->nextRound();
            }

            break;
        case Status::Stop:
            this->setRemainingMilliseconds(this->settings->getRoundMilliseconds());
            this->setRound(0);
        case Status::Pause:
            this->stop();

            break;
        default:
            break;
    }
}

void BoxingTimer::updateRemainingMilliseconds() {
    this->setRemainingMilliseconds(this->mRemainingMilliseconds - 1000);

    if (this->mRemainingMilliseconds < 0) {
        if (this->mStatus == Status::Start) {
            this->mStatus = Status::Rest;
            this->setRemainingMilliseconds(this->settings->getRestMilliseconds());
        } else if(this->mStatus == Status::Rest) {
            this->mStatus = Status::Start;
            this->setRemainingMilliseconds(this->settings->getRoundMilliseconds());

            this->setInnerTimers();

            this->nextRound();
        }

        this->songsBell();
    }

    this->updateInnerRemainingMilliseconds();
}

void BoxingTimer::updateInnerRemainingMilliseconds() {
    if (this->mInnerTime >= 0 && this->mStatus == Status::Start) {
        this->mInnerRemainingMilliseconds -= 1000;

        if (this->mInnerRemainingMilliseconds <= 0) {
            if (++this->mInnerTime >= this->mNumberOfInnerTimers) {
                this->mInnerTime = 0;
            }

            this->mInnerRemainingMilliseconds = this->settings->getInnerTimer(this->mInnerTime);

            this->innerBell->play();
        }
    }
}

void BoxingTimer::setInnerTimers() {
    this->mNumberOfInnerTimers = this->settings->getInnerTimers().size();

    this->mInnerTime = -1;
    if (this->mNumberOfInnerTimers > 0) {
        this->mInnerTime = 0;
        this->mInnerRemainingMilliseconds = this->settings->getInnerTimer(this->mInnerTime);
    }
}

const QString BoxingTimer::remainingTimeToString() {
    return QTime::fromMSecsSinceStartOfDay(this->mRemainingMilliseconds).toString(QString("mm:ss"));
}

void BoxingTimer::nextRound() {
    int newRound = this->mRound + 1;
    int totalRounds = this->settings->getRounds();
    if (totalRounds > 0 && newRound > totalRounds) {
        this->setStatus(Status::Stop);
    } else {
        this->setRound(newRound);
    }
}

void BoxingTimer::restore() {
    if (this->mOldStatus == Status::Default) {
        this->setStatus(Status::Start);
    } else {
        this->setStatus(this->mOldStatus);
    }
}

const QString BoxingTimer::roundsToString() {
    QString s = QString::number(this->mRound) + "/";

    int totalRounds = this->settings->getRounds();
    if (totalRounds > 0) {
        s += QString::number(this->settings->getRounds());
    } else {
        s += QChar(0x221E);
    }

    return s;
}

void BoxingTimer::loadPreset(const QString &preset) {
    this->settings->setPreset(preset);
    this->mRemainingMilliseconds = settings->getRoundMilliseconds();
    this->mStatus = Status::Stop;

    this->setInnerTimers();

    emit presetChanged();
    emit roundsToStringChanged();
    emit remainingTimeToStringChanged();
}

void BoxingTimer::songsBell() {
    if (this->mStatus == Status::Start) {
        this->playBell->play();
    } else if (this->mStatus == Status::Rest) {
        this->restBell->play();
    } else if (this->mStatus == Status::Stop) {
        this->stopBell->play();
    }
}
