/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2003-2020 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
  See COPYING file for copying and redistribution conditions.

  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; version 2 of the License.

  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.
*/
#ifndef DATETIME_H
#define DATETIME_H

#include <cstdio>
#include <cstdint>
#include <vector>
#include <string>

enum class TimeUnit
{
  SECONDS = 0,
  MINUTES,
  HOURS,
  DAYS,
  MONTHS,
  YEARS
};

enum class TimeStat
{
  UNDEF,
  FIRST,
  LAST,
  MEAN,
  MIDHIGH,
};

struct JulianDate
{
  int64_t julday = 0;
  int secofday = 0;
};

struct CdoDateTime
{
  int64_t date = 0;
  int time = 0;
};

struct DateTimeInfo
{
  CdoDateTime c;     // corrected verification time
  CdoDateTime v;     // verification time
  CdoDateTime b[2];  // time bounds
};

class TimeIncrement
{
public:
  int64_t period = 0;
  TimeUnit unit = TimeUnit::SECONDS;

  TimeIncrement() {}
  TimeIncrement(int64_t _period, TimeUnit _unit) : period(_period), unit(_unit) {}
};

struct CheckTimeInc
{
  bool lwarn = true;
  int vdate0 = 0;
  JulianDate juldate0;
  TimeIncrement timeIncr;
};

class DateTimeList
{
public:
  DateTimeList() { init(); }

  void
  setStat(const TimeStat _stat)
  {
    this->stat = _stat;
  }

  void
  setCalendar(const int _calendar)
  {
    this->calendar = _calendar;
  }

  int64_t getVdate(int tsID);
  int getVtime(int tsID);
  void shift();

  void taxisInqTimestep(int taxisID, int tsID);
  void taxisDefTimestep(int taxisID, int tsID);
  void statTaxisDefTimestep(int taxisID, int nsteps);

private:
  size_t nalloc = 0;
  size_t size = 0;
  int has_bounds = -1;
  int calendar = -1;
  TimeStat stat = TimeStat::LAST;
  DateTimeInfo timestat;
  std::vector<DateTimeInfo> dtinfo;

  void init();
  void mean(int nsteps);
  void midhigh(int nsteps);
};

JulianDate julianDateEncode(int calendar, int64_t date, int time);
void julianDateDecode(int calendar, const JulianDate &juldate, int64_t &date, int &time);
JulianDate julianDateSub(const JulianDate &juldate2, const JulianDate &juldate1);
JulianDate julianDateAddSeconds(int64_t seconds, const JulianDate &juldate);
double julianDateToSeconds(const JulianDate &juldate);

void datetime_avg(int dpy, int ndates, CdoDateTime *datetime);
void setTimestatDate(const std::string &optarg);

void adjustMonthAndYear(int &month, int &year);

double deltaTimeStep0(int tsID, int calendar, int64_t vdate, int vtime, JulianDate &juldate0, double &deltat1);

TimeIncrement getTimeIncrement(double jdelta, int64_t vdate0, int64_t vdate1);

void checkTimeIncrement(int tsID, int calendar, int64_t vdate, int vtime, CheckTimeInc &checkTimeInc);

int decodeMonth(int64_t date);
int decodeMonthAndDay(int64_t date);
int decodeDayOfYear(int64_t date);
int decodeHourOfYear(int64_t date, int time);
int decodeHourOfDay(int64_t date, int time);

void setDateTime(CdoDateTime &datetime, int64_t date, int time);

#endif /* DATETIME_H */
