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

  Copyright (C) 2003-2021 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
#ifdef WARN_UNUSED
[[gnu::warn_unused]]
#endif
DateTimeList
{
public:
  DateTimeList() { init(); }
  // clang-format off
  void set_stat(const TimeStat _stat) { this->stat = _stat; }
  void set_calendar(const int _calendar) { this->calendar = _calendar; }
  // clang-format on
  int64_t get_vdate(int tsID);
  int get_vtime(int tsID);
  void shift();

  void taxis_set_next_timestep(int taxisID);
  void taxis_inq_timestep(int taxisID, int tsID);
  void taxisDefTimestep(int taxisID, int tsID);
  void stat_taxis_def_timestep(int taxisID, int nsteps);
  void stat_taxis_def_timestep(int taxisID);

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 julian_date_encode(int calendar, int64_t date, int time);
void julian_date_decode(int calendar, const JulianDate &juldate, int64_t &date, int &time);
JulianDate julian_date_sub(const JulianDate &juldate2, const JulianDate &juldate1);
JulianDate julian_date_add_seconds(int64_t seconds, const JulianDate &juldate);
double julian_date_to_seconds(const JulianDate &juldate);

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

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

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

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

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

int decode_month(int64_t date);
int decode_month_and_day(int64_t date);
int decode_day_of_year(int64_t date);
int decode_hour_of_year(int64_t date, int time);
int decode_hour_of_day(int64_t date, int time);

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

#endif /* DATETIME_H */
