#include <stdarg.h>
#include <stdint.h>
#include "cgribex.h"
#include "grib_int.h"
#include "julian_date.h"

static void
gribDecodeRefDate(const int *isec1, int *year, int *month, int *day)
{
  int ryear = ISEC1_Year;

  int century = ISEC1_Century;
  if (century < 0) century = -century;
  century -= 1;

  if (century == -255 && ryear == 127)
    {
      ryear = 0;
    }
  else
    {
      // if ( century != 0 )
      {
        if (ryear == 100)
          {
            ryear = 0;
            century += 1;
          }

        if (ryear != 255)
          {
            ryear = century * 100 + ryear;
            if (ISEC1_Century < 0) ryear = -ryear;
          }
        else
          {
            ryear = 1;
          }
      }
    }

  *year = ryear;
  *month = ISEC1_Month;
  *day = ISEC1_Day;
}

int
gribRefDate(const int *isec1)
{
  int ryear, rmonth, rday;
  gribDecodeRefDate(isec1, &ryear, &rmonth, &rday);
  return (int) cdiEncodeDate(ryear, rmonth, rday);
}

static void
gribDecodeRefTime(const int *isec1, int *hour, int *minute, int *second)
{
  *hour = ISEC1_Hour;
  *minute = ISEC1_Minute;
  *second = 0;
}

int
gribRefTime(const int *isec1)
{
  int rhour, rminute, rsecond;
  gribDecodeRefTime(isec1, &rhour, &rminute, &rsecond);
  return cdiEncodeTime(rhour, rminute, rsecond);
}

bool
gribTimeIsFC(const int *isec1)
{
  bool isFC = false;

  const int time_period = (ISEC1_TimeRange == 10) ? (ISEC1_TimePeriod1 << 8) + ISEC1_TimePeriod2 : ISEC1_TimePeriod1;

  if (time_period > 0 && ISEC1_Day > 0)
    {
      isFC = (ISEC1_TimeRange == 0 || ISEC1_TimeRange == 10);
    }

  return isFC;
}

static int
getTimeUnitFactor(int timeUnit)
{
  static bool lprint = true;
  // clang-format off
  switch (timeUnit)
    {
    case ISEC1_TABLE4_MINUTE:    return    60; break;
    case ISEC1_TABLE4_QUARTER:   return   900; break;
    case ISEC1_TABLE4_30MINUTES: return  1800; break;
    case ISEC1_TABLE4_HOUR:      return  3600; break;
    case ISEC1_TABLE4_3HOURS:    return 10800; break;
    case ISEC1_TABLE4_6HOURS:    return 21600; break;
    case ISEC1_TABLE4_12HOURS:   return 43200; break;
    case ISEC1_TABLE4_DAY:       return 86400; break;
    default:
      if (lprint)
        {
          gprintf(__func__, "Time unit %d unsupported", timeUnit);
          lprint = false;
        }
      break;
    }
  // clang-format on

  return 0;
}

void
gribDateTimeX(int *isec1, int *date, int *time, int *startDate, int *startTime)
{
  *startDate = 0;
  *startTime = 0;

  CdiDateTime rDateTime = cdiDateTime_set(gribRefDate(isec1), gribRefTime(isec1));

  int64_t time_period = 0, time_period_x = 0;
  if (ISEC1_TimeRange == 10)
    time_period = (ISEC1_TimePeriod1 << 8) + ISEC1_TimePeriod2;
  else if (ISEC1_TimeRange >= 2 && ISEC1_TimeRange <= 5)
    {
      time_period_x = ISEC1_TimePeriod1;
      time_period = ISEC1_TimePeriod2;
    }
  else if (ISEC1_TimeRange == 0)
    time_period = ISEC1_TimePeriod1;

  if (time_period > 0 && rDateTime.date.day > 0)
    {
      JulianDate julianDate = julianDate_encode(CGRIBEX_grib_calendar, rDateTime);

      const int timeUnitFactor = getTimeUnitFactor(ISEC1_TimeUnit);

      if (time_period_x > 0)
        {
          JulianDate julianDate2 = julianDate_add_seconds(julianDate, timeUnitFactor * time_period_x);
          CdiDateTime sDateTime = julianDate_decode(CGRIBEX_grib_calendar, julianDate2);
          sDateTime.time.second = 0;
          *startDate = (int) cdiDate_get(sDateTime.date);
          *startTime = cdiTime_get(sDateTime.time);
        }

      julianDate = julianDate_add_seconds(julianDate, timeUnitFactor * time_period);
      rDateTime = julianDate_decode(CGRIBEX_grib_calendar, julianDate);
    }

  *date = (int) cdiDate_get(rDateTime.date);
  *time = cdiTime_get(rDateTime.time);
}

void
gribDateTime(int *isec1, int *date, int *time)
{
  int sdate, stime;
  gribDateTimeX(isec1, date, time, &sdate, &stime);
}

void
gprintf(const char *caller, const char *fmt, ...)
{
  va_list args;

  if (grprsm == NULL) Error("GRIBEX initialization missing!");

  va_start(args, fmt);

  fprintf(grprsm, "%-18s : ", caller);
  vfprintf(grprsm, fmt, args);
  fputs("\n", grprsm);

  va_end(args);
}
