#ifndef SCSTWTIMER_H
#define SCSTWTIMER_H

#include <QObject>
#include <QDateTime>
#include <QDebug>
#include <QTimer>
#include "ScStw.hpp"

/*!
 * \brief The ScStwTimer class is used for advanced time measurement.
 *
 * It does not work on its own though.
 * It is recommended to use it in combination with the ScStwRace class.
 *
 * ## When using standalone:
 *
 * \code{.cpp}
 * ScStwTimer timer;
 * // start the timer
 * timer.start();
 * // stop the timer
 * timer.stop();
 * \endcode
 *
 * The timer will now go into ScStw::WAITING state.
 * That indicates that the timer has stopped and the final result has to be assigned by an external handler.
 *
 * \code{.cpp}
 * // assign result 'won'
 * timer.setResult(ScStwTimer::WON);
 * \endcode
 *
 * The timer is now in ScStwTimer::WON state.
 *
 * \code{.cpp}
 * // reset the timer
 * timer.reset();
 * \endcode
 *
 * The timer is not in ScStwTimer::IDLE state again.
 */
class ScStwTimer : public QObject
{
    Q_OBJECT
public:
    /*!
     * \brief ScStwTimer constructor
     * \param parent the parent object
     * \param directControlEnabled Defines if protected properties (startTimer, stopTime, reactionTime and state) can be directly set from outside.
     */
    explicit ScStwTimer(QObject *parent = nullptr, bool directControlEnabled = false);

    /*!
     * \brief The TimerState enum contains all state the timer can be in
     */
    enum TimerState {
        IDLE,       /*!< Timer is waiting to be started */
        STARTING,   /*!< Timer is starting and will react with a false start if the climber starts */
        RUNNING,    /*!< Timer is running */
        WAITING,    /*!< Timer was stopped and is waiting for the result */
        WON,        /*!< Timer has won */
        LOST,       /*!< Timer has lost */
        FAILED,     /*!< A false start occured */
        CANCELLED,  /*!< Timer was cancelled */
        DISABLED    /*!< Timer is disabled */
    };
    Q_ENUM(TimerState);

    /*!
     * \brief The StopReason enum contains all possible reasons for a stop
     */
    enum StopReason {
        ManualStop, /*!< Timer was stopped manually */
        CancelStop, /*!< Timer was cancelled */
        FailStop,   /*!< A false start occured */
        TopPadStop  /*!< Timer was stopped by a physical trigger (eg. a ScStwExtension) */
    };

protected:
    /*!
     * \brief The current state of the timer
     */
    TimerState state;

    /*!
     * \brief The time the timer was started at
     */
    double startTime;

    /*!
     * \brief The time the timer was stopped at
     */
    double stopTime;

    /*!
     * \brief the reaction time of the climber
     */
    double reactionTime;

    /*!
     * \brief Defines if protected properties (startTimer, stopTime, reactionTime and state) can be directly set from outside.
     */
    bool directControlEnabled;

public slots:

    /*!
     * \brief Function to start the timer
     *
     * To do this, the timer has to be in ScStwTimer::STARTING state!
     *
     * \return false if the timer was not in the required state and therefore not started, true otherwise
     */
    bool start();

    /*!
     * \brief Function to start the timer at a given point in time (present or future)
     *
     * To do this, the timer has to be in ScStwTimer::STARTING state!
     *
     * \param timeOfStart the point in time (msecs since epoch) when the timer is supposted to be started
     * \return false if the timer was not in the required state and therefore not started, true otherwise
     */
    virtual bool start(double timeOfStart);

    /*!
     * \brief Function to cancel the timer
     *
     * To do this, the timer has to be in ScStwTimer::IDLE, ScStwTimer::STARTING or ScStwTimer::RUNNING state!
     *
     * \return false if the timer was not in the required state and therefore not cancelled, true otherwise
     */
    bool cancel();

    /*!
     * \brief Function to stop the timer
     *
     * To do this, the timer has to be in ScStwTimer::RUNNING state!
     *
     * \return false if the timer was not in the required state and therefore not stopped, true otherwise
     */
    bool stop();

    /*!
     * \brief Function to stop the timer at a given point in time (past or future)
     *
     * To do this, the timer has to be in ScStwTimer::RUNNING state!
     *
     * \param timeOfStop the point in time (msecs since epoch) when the timer is supposted to be stopped
     *
     * \return false if the timer was not in the required state and therefore not stopped, true otherwise
     */
    bool stop(double timeOfStop);

    /*!
     * \brief Function to assing the result of the race to the timer
     *
     * To do this, the timer has to be in ScStwTimer::WAITING state!
     *
     * \return false if the timer was not in the required state and the result therefore not set, true otherwise
     */
    bool setResult(TimerState);

    /*!
     * \brief Function to reset the timer
     *
     * To do this, the timer has to be in ScStwTimer::WON or ScSTw::LOST state!
     *
     * \return false if the timer was not in the required state and therefore not reset, true otherwise
     */
    virtual bool reset();

    // -- helper functions --
    /*!
     * \brief Function to get the current state of the timer
     * \return current state of the timer
     * \see ScStwTimer::TimerState
     */
    TimerState getState();

    /*!
     * \brief Function to get the current time of the timer
     *
     * To do this, the timer has to be in ScStwTimer::RUNNING, ScStwTimer::WAITING, ScStwTimer::WON or ScSTw::LOST state!
     *
     * \return The current / final time of the timer or -1 if it is not in the required state
     */
    double getCurrentTime();

    /*!
     * \brief Function to get the reaction time of the climber.
     * \return The climbers reaction time
     */
    double getReactionTime();

    /*!
     * \brief Function to get the text, a timer display is supposed to show
     * \return The text to show
     */
    QString getText();

    /*!
     * \brief Function to set if the timer is supposed to be disabled
     *
     * !!! CAUTION use this function with care, it immidiately changes the state of the timer !!!
     * It is recommended to only use this function to change the timers state after the
     * ScStwTimer::requestTimerEnableChange() signal was called, during the race,
     * the timer is used in, is in IDLE state.
     *
     * \param disabled if the timer is supposed to be diabled
     */
    void setDisabled(bool disabled);

    /*!
     * \brief Function to change the state of the timer
     *
     * Only works when directControlEnabled is set to true!
     * Doing this will emit the ScStwTimer::stateChanged() signal (only if the new state differs from the current one)
     *
     * \param newState The new state
     * \param force whether to force the state change (just to distinguish between protected and public function)
     * \return false when directControlEnabled or force is set to false and the startTime was therefore not modified, true otherwise
     */
    bool setState(TimerState newState, bool force);

    /*!
     * \brief Function to dircetly change the start time
     *
     * Only works when directControlEnabled is set to true!
     *
     * \param startTime the time to change to
     * \return false when directControlEnabled is set to false and the startTime was therefore not modified, true otherwise
     */
    bool setStartTime(double startTime);

    /*!
     * \brief Function to dircetly change the stop time
     *
     * Only works when directControlEnabled is set to true!
     *
     * \param stopTime the time to change to
     * \return false when directControlEnabled is set to false and the stopTime was therefore not modified, true otherwise
     */
    bool setStopTime(double stopTime);

    /*!
     * \brief Function to dircetly change the rection time
     *
     * Only works when directControlEnabled is set to true!
     *
     * \param reactionTime the time to change to
     * \return false when directControlEnabled is set to false and the reactionTime was therefore not modified, true otherwise
     */
    bool setReactionTime(double rectionTime);

protected slots:

    /*!
     * \brief slot to call when the climber has started
     * \param timeOfStart time (msecs since epoch) when the climber started
     */
    void handleClimberStart(double timeOfStart);

    /*!
     * \brief Function to stop the timer at a given point in time (past or future)
     *
     * To do this, the timer has to be in ScStwTimer::RUNNING state!
     *
     * \param reason reason for the timer stop
     * \param timeOfStop the point in time (msecs since epoch) when the timer is supposted to be stopped
     *
     * \return false if the timer was not in the required state and therefore not stopped, true otherwise
     *
     * \see ScStwTimer::StopReason
     */
    virtual bool stop(StopReason reason, double timeOfStop);

    /*!
     * \brief Function to stop the timer
     *
     * To do this, the timer has to be in ScStwTimer::RUNNING state!
     *
     * \param reason reason for the timer stop
     *
     * \return false if the timer was not in the required state and therefore not stopped, true otherwise
     *
     * \see ScStwTimer::StopReason
     */
    bool stop(StopReason reason);

    /*!
     * \brief Function to change the state of the timer
     *
     * Doing this will emit the ScStwTimer::stateChanged() signal (only if the new state differs from the current one)
     *
     * \param newState The new state
     */
    void setState(TimerState newState);


signals:
    /*!
     * \brief Emitted when the state of the timer changed
     */
    void stateChanged();

    /*!
     * \brief Emitted when the reaction time changed
     */
    void reactionTimeChanged();

    /*!
     * \brief Emitted when the timer wants its state to be changed by the external handler
     * \param timer the timer object
     */
    void requestEnableChange(ScStwTimer* timer);

};

#endif // SCSTWTIMER_H
