/****************************************************************************
 ** 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/>.
 ****************************************************************************/

#ifndef SCSTWRACE_H
#define SCSTWRACE_H

#include <QObject>
#include <QDebug>
#include <QTimer>
#include <QEventLoop>
#include "scstwtimer.h"
#include "scstwsoundplayer.h"
#include "scstwsettings.h"

class ScStwRemoteRace;

/*!
 * \brief The ScStwRace class can be used to measure timings of climbing races with multiple lanes at once.
 *
 * The ScStwRace is a container to manage multiple timers at a time and introduces a propper start sequence
 * with start commands ('At your Marks' and 'Ready') and the official IFSC start signal.
 *
 * ## Basic usage:
 *
 * \code
 * ScStwRace race;
 *
 * // add two timers
 * race.addTimer(new ScStwTimer());
 * race.addTimer(new ScStwTimer());
 *
 * // start a race
 * race.start();
 * \endcode
 *
 */
class ScStwRace : public QObject
{
    Q_OBJECT
    Q_PROPERTY(RaceState state READ getState NOTIFY stateChanged)
    Q_PROPERTY(QVariantList timers READ getTimerDetailList NOTIFY timersChanged)
    Q_PROPERTY(QVariantMap currentStartDelay READ getCurrentStartDelay NOTIFY currentStartDelayChanged)
    Q_PROPERTY(bool isReadyForNextState READ getIsReadyForNextState NOTIFY isReadyForNextStateChanged)
    Q_PROPERTY(bool competitionMode READ getCompetitionMode NOTIFY competitionModeChanged)
    Q_PROPERTY(bool readySoundEnabled READ getReadySoundEnabled NOTIFY readySoundEnabledChanged)
    Q_PROPERTY(QVariantMap details READ getDetails NOTIFY detailsChanged)
    Q_PROPERTY(ScStwSettings* settings READ getSettings WRITE setSettings NOTIFY settingsChanged)
    Q_PROPERTY(bool autoRefreshTimerText READ getAutoRefreshTimerText WRITE setAutoRefreshTimerText NOTIFY autoRefreshTimerTextChanged)

public:
    explicit ScStwRace(QObject *parent = nullptr);
    explicit ScStwRace(ScStwSettings *settings, QObject *parent = nullptr);

    friend class ScStwRemoteRace;

    enum RaceState { IDLE, PREPAIRING, WAITING, STARTING, RUNNING, STOPPED, INCIDENT };
    Q_ENUM(RaceState)

protected:
    QList<ScStwTimer *> timers;
    void setState(RaceState newState);

private:
    RaceState state;

    QTimer *startDelayTimer;
    QTimer *timerTextRefreshTimer;
    QEventLoop *startWaitLoop;

    // sounds
    ScStwSoundPlayer * soundPlayer;

    // settings
    ScStwSettings *settings;
    bool competitionMode;
    bool autoRefreshTimerText;

    enum LoopExitTypes {
        LoopAutomaticExit = 0,
        LoopReadyStateChangeExit = 1,
        LoopManualExit = 2,
        LoopCancelExit = 3
    };


public slots:
    /*!
     * \brief Function to start the race
     *
     * \param asyncronous if the function should just start the start sequence and then quit (true)
     *                    or if if should wait until the start sequence is over and quit after that (false)
     * \return 200: OK; 904: state not matching
     */
    virtual ScStw::StatusCode start(bool asyncronous = true);

    /*!
     * \brief Function to stop the currently running race
     *
     * \return 200: OK; 904: state not matching
     */
    virtual ScStw::StatusCode stop();

    /*!
     * \brief Function to reset a stopped race
     * \return
     */
    virtual ScStw::StatusCode reset();
    virtual ScStw::StatusCode cancel();

    virtual ScStw::StatusCode setTimerDisabled(int id, bool disabled);

    Q_INVOKABLE virtual bool addTimer(ScStwTimer *timer);

    // getters
    RaceState getState();
    virtual QVariantMap getCurrentStartDelay();
    QList<ScStwTimer*> getTimers();
    QVariantList getTimerDetailList();
    QVariantMap getDetails();
    bool getCompetitionMode();
    virtual bool getReadySoundEnabled();

    ScStwSettings* getSettings();
    void setSettings(ScStwSettings* settings);

    bool getAutoRefreshTimerText();
    void setAutoRefreshTimerText(bool autoRefresh);

protected slots:

private slots:
    void handleTimerStateChange(ScStwTimer::TimerState newState);

    /**
     * \brief Function to declare the winner and looser timers after a timer has been stopped
     */
    void handleTimerStop();
    void handleFalseStart();


    void handleTimerWantsToBeDisabledChange(ScStwTimer* timer, bool wantsToBeDisabled);
    bool playSoundsAndStartTimers();
    ScStwSoundPlayer::PlayResult doDelayAndSoundOfCurrentStartState(double *timeOfSoundPlaybackStart = nullptr);
    void technicalIncident();
    ScStw::StatusCode setTimerDisabled(ScStwTimer* timer, bool disabled);

    virtual void refreshCompetitionMode();

    double getSoundVolume();
    ScStwSoundPlayer::StartSound getSoundForState(ScStwRace::RaceState state);
    bool getSoundEnabledSetting(ScStwSoundPlayer::StartSound sound);
    int getSoundDelaySetting(ScStwSoundPlayer::StartSound sound);


    bool isStarting();
    virtual bool getIsReadyForNextState();
    void handleTimerReadyStateChange(ScStwTimer::ReadyState readyState);

signals:
    void startTimers();
    void stopTimers(int type);
    void resetTimers();
    void stateChanged(RaceState state);
    void currentStartDelayChanged();
    void timersChanged();
    void isReadyForNextStateChanged();
    void detailsChanged();
    void competitionModeChanged();
    void readySoundEnabledChanged();
    void settingsChanged();
    void autoRefreshTimerTextChanged();

};

#endif // SCSTWRACE_H
