#ifndef INTERVAL_HH
#define INTERVAL_HH

#include <QString>
#include <QMetaType>
#include <yaml-cpp/yaml.h>

/** Represents a time interval.
 * @ingroup utils */
class Interval
{
public:
  /** Possible formats. */
  enum class Format {
    Automatic, Milliseconds, Seconds, Minutes
  };

protected:
  /** Constructor from milliseconds. */
  constexpr explicit Interval(unsigned long long ms)
    : _duration(ms)
  {
    // pass...
  }

public:
  /** Default constructor. */
  Interval()
    : _duration(0)
  {
    // pass...
  }

  /** Copy constructor. */
  constexpr Interval(const Interval &other)
    : _duration(other._duration)
  {
    // pass...
  }

  inline Interval &operator =(const Interval &other) {   ///< Assignment.
    _duration = other._duration; return *this;
  }

  inline bool isNull() const { return 0 == _duration; }  ///< Test for 0.
  /** Test for infinite durations. */
  inline bool isInfinite() const {
    return std::numeric_limits<unsigned long long>::max() == _duration;
  }
  /** Test for finite values. */
  inline bool isFinite() const { return (!isNull()) && (!isInfinite()); }

  inline bool operator ==(const Interval &other) const { ///< Comparison.
    return _duration == other._duration;
  }
  inline bool operator<  (const Interval &other) const {///< Comparison.
    return _duration < other._duration;
  }
  inline bool operator<=  (const Interval &other) const {///< Comparison.
    return _duration <= other._duration;
  }
  inline bool operator>  (const Interval &other) const {///< Comparison.
    return _duration > other._duration;
  }
  inline bool operator>=  (const Interval &other) const {///< Comparison.
    return _duration >= other._duration;
  }

  inline unsigned long long milliseconds() const { return _duration; }      ///< Unit conversion.
  inline unsigned long long seconds() const { return _duration/1000ULL; }   ///< Unit conversion.
  inline unsigned long long minutes() const { return _duration/60000ULL; }  ///< Unit conversion.

  static inline constexpr Interval fromMilliseconds(unsigned long long ms) {          ///< Unit conversion.
    return Interval(ms);
  }
  static inline constexpr Interval fromSeconds(unsigned long long s) {                ///< Unit conversion.
    return Interval(s*1000ULL);
  }
  static inline constexpr Interval fromMinutes(unsigned long long min) {              ///< Unit conversion.
    return Interval(min*60000ULL);
  }

  /** Format the frequency. */
  QString format(Format f=Format::Automatic) const;
  /** Parses a frequency. */
  bool parse(const QString &value, Format defaultFormat=Format::Milliseconds);

public:
  /** Constructs a null interval. */
  static inline Interval null() { return Interval(0); }
  /** Constructs an infinite interval. */
  static inline Interval infinity() {
    return Interval(std::numeric_limits<unsigned long long>::max());
  }

private:
  /** An interval duration in ms. */
  unsigned long long _duration;
};

Q_DECLARE_METATYPE(Interval)


namespace YAML
{
  /** Implements the conversion to and from YAML::Node. */
  template<>
  struct convert<Interval>
  {
    /** Serializes the interval. */
    static Node encode(const Interval& rhs) {
      return Node(rhs.format().toStdString());
    }

    /** Parses the interval. */
    static bool decode(const Node& node, Interval& rhs) {
      if (!node.IsScalar())
        return false;
      return rhs.parse(QString::fromStdString(node.as<std::string>()));
    }
  };
}


#endif // INTERVAL_HH
