//! \~\file pidatetime.h
//! \~\ingroup Types
//! \~\brief
//! \~english Time and date structs
//! \~russian Типы времени и даты
/*
    PIP - Platform Independent Primitives
    Time and date structs
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIDATETIME_H
#define PIDATETIME_H


#include "pisystemtime.h"


//! \~\ingroup Types
//! \~\brief
//! \~english Calendar time.
//! \~russian Календарное время.
class PIP_EXPORT PITime {
public:
	//! \~english Construct %PITime from hours, minutes, seconds and milliseconds
	//! \~russian Создает %PITime из часов, минут, секунд и миллисекунд
	PITime(int hours_ = 0, int minutes_ = 0, int seconds_ = 0, int milliseconds_ = 0)
		: hours(hours_)
		, minutes(minutes_)
		, seconds(seconds_)
		, milliseconds(milliseconds_) {
		;
	}

	//! \~english Returns string representation
	//! \~russian Возвращает строковое представление
	PIString toString(const PIString & format = "h:mm:ss") const;

	//! \~english Returns time as %PISystemTime
	//! \~russian Возвращает время как %PISystemTime
	PISystemTime toSystemTime() const;

	//! \~english Returns %PITime from string representation
	//! \~russian Возвращает %PITime из строкового представления
	static PITime fromString(PIString string, PIString format = "h:mm:ss");

	//! \~english Returns current time
	//! \~russian Возвращает текущее время
	static PITime current();

	//! \~english Construct %PITime from %PISystemTime
	//! \~russian Создает %PITime из %PISystemTime
	static PITime fromSystemTime(const PISystemTime & st);


	//! \~english Hour, 0-23
	//! \~russian Час, 0-23
	int hours;

	//! \~english Minutes, 0-59
	//! \~russian Минуты, 0-59
	int minutes;

	//! \~english Seconds, 0-59
	//! \~russian Секунды, 0-59
	int seconds;

	//! \~english Milliseconds, 0-999
	//! \~russian Миллисекунды, 0-999
	int milliseconds;
};

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator==(const PITime & t0, const PITime & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator<(const PITime & t0, const PITime & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator>(const PITime & t0, const PITime & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator!=(const PITime & t0, const PITime & t1) {
	return !(t0 == t1);
}

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator<=(const PITime & t0, const PITime & t1) {
	return !(t0 > t1);
}

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator>=(const PITime & t0, const PITime & t1) {
	return !(t0 < t1);
}

//! \relatesalso PICout
//! \~english \brief Output operator to PICout
//! \~russian \brief Оператор вывода в PICout
PIP_EXPORT PICout operator<<(PICout s, const PITime & v);


//! \~\ingroup Types
//! \~\brief
//! \~english Calendar date.
//! \~russian Календарная дата.
class PIP_EXPORT PIDate {
public:
	//! \~english Construct %PIDate from year, month and day
	//! \~russian Создает %PIDate из года, месяца и дня
	PIDate(int year_ = 0, int month_ = 0, int day_ = 0): year(year_), month(month_), day(day_) { ; }

	//! \~english Returns string representation
	//! \~russian Возвращает строковое представление
	PIString toString(const PIString & format = "d.MM.yyyy") const;

	//! \~english Returns %PIDate from string representation
	//! \~russian Возвращает %PIDate из строкового представления
	static PIDate fromString(PIString string, PIString format = "d.MM.yyyy");

	//! \~english Returns current date
	//! \~russian Возвращает текущую дату
	static PIDate current();


	//! \~english Year
	//! \~russian Год
	int year;

	//! \~english Month, 1-12
	//! \~russian Месяц, 1-12
	int month;

	//! \~english Day, 1-31
	//! \~russian День, 1-31
	int day;
};

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator==(const PIDate & t0, const PIDate & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator<(const PIDate & t0, const PIDate & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator>(const PIDate & t0, const PIDate & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator!=(const PIDate & t0, const PIDate & t1) {
	return !(t0 == t1);
}

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator<=(const PIDate & t0, const PIDate & t1) {
	return !(t0 > t1);
}

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator>=(const PIDate & t0, const PIDate & t1) {
	return !(t0 < t1);
}

//! \relatesalso PICout
//! \~english \brief Output operator to PICout
//! \~russian \brief Оператор вывода в PICout
PIP_EXPORT PICout operator<<(PICout s, const PIDate & v);


//! \~\ingroup Types
//! \~\brief
//! \~english Calendar date and time.
//! \~russian Календарное дата и время.
class PIP_EXPORT PIDateTime {
public:
	//! \~english Construct null %PIDateTime
	//! \~russian Создает нулевой %PIDateTime
	PIDateTime() { year = month = day = hours = minutes = seconds = milliseconds = 0; }

	//! \~english Construct %PIDateTime from %PITime and null %PIDate
	//! \~russian Создает %PIDateTime из %PITime и нулевого %PIDate
	PIDateTime(const PITime & time) {
		year = month = day = 0;
		hours              = time.hours;
		minutes            = time.minutes;
		seconds            = time.seconds;
		milliseconds       = time.milliseconds;
	}

	//! \~english Construct %PIDateTime from %PIDate and null %PITime
	//! \~russian Создает %PIDateTime из %PIDate и нулевого %PITime
	PIDateTime(const PIDate & date) {
		year  = date.year;
		month = date.month;
		day   = date.day;
		hours = minutes = seconds = milliseconds = 0;
	}

	//! \~english Construct %PIDateTime from %PIDate and %PITime
	//! \~russian Создает %PIDateTime из %PIDate и %PITime
	PIDateTime(const PIDate & date, const PITime & time) {
		year         = date.year;
		month        = date.month;
		day          = date.day;
		hours        = time.hours;
		minutes      = time.minutes;
		seconds      = time.seconds;
		milliseconds = time.milliseconds;
	}


	//! \~english Returns normalized %PIDateTime
	//! \~russian Возвращает нормализованный %PIDateTime
	PIDateTime normalized() const { return PIDateTime::fromSecondSinceEpoch(toSecondSinceEpoch()); }

	//! \~english Normalize all fields
	//! \~russian Нормализует все поля
	void normalize() { *this = normalized(); }

	//! \~english Returns string representation
	//! \~russian Возвращает строковое представление
	PIString toString(const PIString & format = "d.MM.yyyy h:mm:ss") const;

	//! \~english Returns seconds since 1 Jan 1970
	//! \~russian Возвращает секунды от 1 Янв 1970
	time_t toSecondSinceEpoch() const;

	//! \~english Returns %PIDateTime from string representation
	//! \~russian Возвращает %PIDateTime из строкового представления
	static PIDateTime fromString(PIString string, PIString format = "d.MM.yyyy h:mm:ss");

	//! \~english Returns time as %PISystemTime
	//! \~russian Возвращает время как %PISystemTime
	PISystemTime toSystemTime() const;

	//! \~english Returns date part
	//! \~russian Возвращает дату
	PIDate date() const { return PIDate(year, month, day); }

	//! \~english Returns time part
	//! \~russian Возвращает время
	PITime time() const { return PITime(hours, minutes, seconds, milliseconds); }

	//! \~english Set date part
	//! \~russian Устанавливает дату
	void setDate(const PIDate & d) {
		year  = d.year;
		month = d.month;
		day   = d.day;
	}

	//! \~english Set time part
	//! \~russian Устанавливает время
	void setTime(const PITime & t) {
		hours        = t.hours;
		minutes      = t.minutes;
		seconds      = t.seconds;
		milliseconds = t.milliseconds;
	}

	//! \~english Sum operator
	//! \~russian Оператор сложения
	void operator+=(const PIDateTime & d1) {
		year += d1.year;
		month += d1.month;
		day += d1.day;
		hours += d1.hours;
		minutes += d1.minutes;
		seconds += d1.seconds;
		normalize();
	}

	//! \~english Subtract operator
	//! \~russian Оператор вычитания
	void operator-=(const PIDateTime & d1) {
		year -= d1.year;
		month -= d1.month;
		day -= d1.day;
		hours -= d1.hours;
		minutes -= d1.minutes;
		seconds -= d1.seconds;
		normalize();
	}

	//! \~english Construct %PIDateTime from seconds since 1 Jan 1970
	//! \~russian Создает %PIDateTime из секунд от 1 Янв 1970
	static PIDateTime fromSecondSinceEpoch(const time_t sec);

	//! \~english Construct %PIDateTime from %PISystemTime
	//! \~russian Создает %PIDateTime из %PISystemTime
	static PIDateTime fromSystemTime(const PISystemTime & st) {
		PIDateTime dt   = fromSecondSinceEpoch(st.seconds);
		dt.milliseconds = piClampi(st.nanoseconds / 1000000, 0, 999);
		return dt;
	}

	//! \~english Returns current date and time
	//! \~russian Возвращает текущую дату и время
	static PIDateTime current();


	//! \~english Year
	//! \~russian Год
	int year;

	//! \~english Month, 1-12
	//! \~russian Месяц, 1-12
	int month;

	//! \~english Day, 1-31
	//! \~russian День, 1-31
	int day;

	//! \~english Hour, 0-23
	//! \~russian Час, 0-23
	int hours;

	//! \~english Minutes, 0-59
	//! \~russian Минуты, 0-59
	int minutes;

	//! \~english Seconds, 0-59
	//! \~russian Секунды, 0-59
	int seconds;

	//! \~english Milliseconds, 0-999
	//! \~russian Миллисекунды, 0-999
	int milliseconds;
};

//! \~english Sum operator
//! \~russian Оператор сложения
inline PIDateTime operator+(const PIDateTime & d0, const PIDateTime & d1) {
	PIDateTime td = d0;
	td += d1;
	return td.normalized();
}

//! \~english Subtract operator
//! \~russian Оператор вычитания
inline PIDateTime operator-(const PIDateTime & d0, const PIDateTime & d1) {
	PIDateTime td = d0;
	td -= d1;
	return td.normalized();
}

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator==(const PIDateTime & t0, const PIDateTime & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator<(const PIDateTime & t0, const PIDateTime & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
PIP_EXPORT bool operator>(const PIDateTime & t0, const PIDateTime & t1);

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator!=(const PIDateTime & t0, const PIDateTime & t1) {
	return !(t0 == t1);
}

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator<=(const PIDateTime & t0, const PIDateTime & t1) {
	return !(t0 > t1);
}

//! \~english Compare operator
//! \~russian Оператор сравнения
inline bool operator>=(const PIDateTime & t0, const PIDateTime & t1) {
	return !(t0 < t1);
}

//! \relatesalso PICout
//! \~english \brief Output operator to PICout
//! \~russian \brief Оператор вывода в PICout
PIP_EXPORT PICout operator<<(PICout s, const PIDateTime & v);


#endif // PIDATETIME_H
