/***************************************************************************
**
**  This file is part of GeopsyCore.
**
**  GeopsyCore 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.
**
**  GeopsyCore 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2003-09-17
**  Copyright: 2003-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef TIMERANGE_H
#define TIMERANGE_H

#include <QGpCoreTools.h>

#include "GeopsyCoreDLLExport.h"

namespace GeopsyCore {

class GEOPSYCORE_EXPORT TimeRange
{
public:
  TimeRange() {}
  inline TimeRange(const TimeRange& o) {operator=(o);}
  inline TimeRange(const DateTime& start, const DateTime& end);
  inline TimeRange(const DateTime& start, double lengthSeconds);
  ~TimeRange() {}

  inline void operator=(const TimeRange& o);
  inline bool operator==(const TimeRange& o) const;
  inline bool operator<(const TimeRange& o) const;

  const DateTime& start() const {return _start;}
  const DateTime& end() const {return _end;}
  DateTime start(double s) const {DateTime t(_start); t.addFewSeconds(s); return t;}
  DateTime end(double s) const {DateTime t(_end); t.addFewSeconds(s); return t;}
  DateTime center() const {return _start.average(_end);}

  void setStart(const DateTime& t) {_start=t;}
  void setEnd(const DateTime& t) {_end=t;}
  inline void sort();

  inline void shift(double s);
  inline void round(const DateTime& rt, double period) {round(rt, period, 1.0/period);}
  inline void round(const DateTime& rt, double period, double frequency);
  inline TimeRange shifted(double s) const;
  inline void scale(const DateTime& center, double factor);
  inline void truncate(double s);
  inline TimeRange truncated(double s) const;

  int lengthSamples(double samplingFrequency) const {return qRound(lengthSeconds()*samplingFrequency);}
  double lengthSeconds() const {return _start.secondsTo(_end);}

  inline TimeRange intersection(const TimeRange& tw) const;
  inline bool intersects(const TimeRange& tw) const;
  bool contains(const DateTime& t) const {return _start<=t && t<=_end;}

  QString toString() const;

  void printDebug() const;
private:
  DateTime _start;
  DateTime _end;
};

inline TimeRange::TimeRange(const DateTime& start, const DateTime& end)
{
  if(start>end) {
    _start=end;
    _end=start;
  } else {
    _start=start;
    _end=end;
  }
}

inline TimeRange::TimeRange(const DateTime& start, double lengthSeconds)
{
  _start=start;
  _end=start;
  _end.addSeconds(lengthSeconds);
}

inline void TimeRange::operator=(const TimeRange& o)
{
  _start=o._start;
  _end=o._end;
}

inline bool TimeRange::operator==(const TimeRange& o) const
{
  return _start==o._start && _end==o._end;
}

inline bool TimeRange::operator<(const TimeRange& o) const
{
  return _start<o._start;
}

inline void TimeRange::sort()
{
  if(_start>_end) {
    qSwap(_start, _end);
  }
}

inline void TimeRange::shift(double s)
{
  _start+=s;
  _end+=s;
}

inline void TimeRange::round(const DateTime& rt, double period, double frequency)
{
  double s;
  s=qCeil(rt.secondsTo(_start)*frequency)*period;
  _start=rt;
  _start+=s;
  s=qFloor(rt.secondsTo(_end)*frequency)*period;
  _end=rt;
  _end+=s;
}

inline void TimeRange::truncate(double s)
{
  if(lengthSeconds()>s) {
    _end=_start;
    _end+=s;
  }
}

inline TimeRange TimeRange::truncated(double s) const
{
  if(lengthSeconds()>s) {
    return TimeRange(_start, s);
  } else {
    return *this;
  }
}

inline TimeRange TimeRange::shifted(double s) const
{
  return TimeRange(_start+s, _end+s);
}

inline void TimeRange::scale(const DateTime& center, double factor)
{
  double s;
  s=center.secondsTo(_start);
  _start=center;
  _start+=s*factor;
  s=center.secondsTo(_end);
  _end=center;
  _end+=s*factor;
}

inline TimeRange TimeRange::intersection(const TimeRange& tw) const
{
  TimeRange res;
  if(tw._end < _start || tw._start > _end)
    return res;
  if(tw._start < _start && _start < tw._end)
    res._start=_start;
  else
    res._start=tw._start;
  if(tw._start < _end && _end < tw._end)
    res._end=_end;
  else
    res._end=tw._end;
  return res;
}

inline bool TimeRange::intersects(const TimeRange& tw) const
{
  if(tw._end < _start || tw._start > _end)
    return false;
  return true;
}

} // namespace GeopsyCore

// Allow passing TimeRange as a QVariant or through SIGNAL/SLOT
Q_DECLARE_METATYPE(GeopsyCore::TimeRange);

#endif // TIMERANGE_H
