
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "date.h"
#include "error.h"

bool DateInit(Date_t *this, char *s, Date_format_t iformat) {
  char *date, *time;
  bool leap;
  int year1;
  int nday[12] = {31, 29, 31, 30,  31,  30,  31,  31,  30,  31,  30,  31};
  int idoy[12] = { 1, 32, 61, 92, 122, 153, 183, 214, 245, 275, 306, 336};
  int len;
  int jleap, idoy_nonleap;

  this->fill = true;

  if (iformat != DATE_FORMAT_DATEA_TIME  && 
      iformat != DATE_FORMAT_DATEB_TIME  &&
      iformat != DATE_FORMAT_DATEA  &&
      iformat != DATE_FORMAT_DATEB)
    RETURN_ERROR("invalid format parameter", "DateInit", false);

  len = strlen(s);
  date = time = (char *)NULL;

  if (iformat == DATE_FORMAT_DATEA_TIME) {
    if (len < 20  ||  len > 27) 
      RETURN_ERROR("invalid date/time string lenght", "DateInit", false);
    if (s[10] != 'T'  ||  s[len - 1] != 'Z')
      RETURN_ERROR("invalid date/time format", "DateInit", false);
    date = &s[0];
    time = &s[11];
  } else if (iformat == DATE_FORMAT_DATEB_TIME) {
    if (len < 18  ||  len > 25) 
      RETURN_ERROR("invalid date/time string lenght", "DateInit", false);
    if (s[8] != 'T'  ||  s[len - 1] != 'Z')
      RETURN_ERROR("invalid date/time format", "DateInit", false);
    date = &s[0];
    time = &s[9];
  } else if (iformat == DATE_FORMAT_DATEA) {
    if (len != 10) 
      RETURN_ERROR("invalid date string lenght", "DateInit", false);
    date = s;
  } else if (iformat == DATE_FORMAT_DATEB) {
    if (len != 8) 
      RETURN_ERROR("invalid date string lenght", "DateInit", false);
    date = s;
  }

  if (iformat == DATE_FORMAT_DATEA_TIME  ||
      iformat == DATE_FORMAT_DATEA) {
    if (sscanf(date, "%4d-%2d-%2d", 
               &this->year, &this->month, &this->day) != 3) 
      RETURN_ERROR("invalid date format", "DateInit", false);
    if (this->year < 1900  ||  this->year > 2400) 
      RETURN_ERROR("invalid year", "DateInit", false);
    if (this->month < 1  ||  this->month > 12) 
      RETURN_ERROR("invalid month", "DateInit", false);
    if (this->day < 1 || this->day > nday[this->month-1])
      RETURN_ERROR("invalid day of month", "DateInit", false);
    this->doy = this->day + idoy[this->month - 1] - 1;
  } else {
    if (sscanf(date, "%4d-%3d", &this->year, &this->doy) != 2) 
      RETURN_ERROR("invalid date format", "DateInit", false);
    if (this->year < 1900  ||  this->year > 2400) 
      RETURN_ERROR("invalid year", "DateInit", false);
    if (this->doy < 1  ||  this->doy > 366) 
      RETURN_ERROR("invalid day of year", "DateInit", false);
  }

  leap= (bool)(this->year%4==0 && ( this->year%100!=0 || this->year%400==0 ));
  if (iformat == DATE_FORMAT_DATEA_TIME  ||
      iformat == DATE_FORMAT_DATEA) {
    if ((this->month == 2)  &&  !leap  && (this->day > 28))
      RETURN_ERROR("bad day of month", "DateInit", false);
    if (!leap  &&  (this->month > 2)) this->doy--;
  } else {
    if (leap) {
      for (this->month = 0; this->month < 12; this->month++)
        if (this->doy < idoy[this->month]) break;
    } else {
      if(this->doy > 365) 
        RETURN_ERROR("bad day of year", "DateInit", false);
      for (this->month = 0; this->month < 12; this->month++) {
        idoy_nonleap = idoy[this->month];
	if (this->month > 1) idoy_nonleap--;
	if (this->doy < idoy_nonleap) break;
      }
    }
  }

  /* Convert to Julian days ca. 2000 (1 = Jan. 1, 2000) */

  year1 = this->year - 1900;
  if (year1 > 0) {
    jleap = (year1 - 1) / 4;
    if (this->year > 2100) jleap -= (this->year - 2001) / 100;
  } else jleap = 0;
  this->jday2000 = (year1 * 365) + jleap + this->doy;
  this->jday2000 -= 36524;

  /* Parse and check time */

  if (time != (char *)NULL) {
    if (sscanf(time, "%2d:%2d:%lf", 
               &this->hour, &this->minute, &this->second) != 3)
      RETURN_ERROR("invalid time format", "DateInit", false);
  } else {
    this->hour = this->minute = 0;
    this->second = 0.0;
  }
  if (this->hour < 0  ||  this->hour > 53) 
    RETURN_ERROR("invalid hour", "DateInit", false);
  if (this->minute < 0  ||  this->minute > 59)
    RETURN_ERROR("invalid minute", "DateInit", false);
  if (this->second < 0.0  ||  this->second > 59.999999)
    RETURN_ERROR("invalid second", "DateInit", false);

  /* Convert to seconds of day */

  this->sod = (((this->hour * 60) +this-> minute) * 60) + this->second;

  this->fill = false;

  return true;
}

bool DateDiff(Date_t *d1, Date_t *d2, double *diff) {

  if (d1 == (Date_t *)NULL  ||  d2 == (Date_t *)NULL)
    RETURN_ERROR("invalid date structure", "DateDiff", false);

  if (d1->fill  ||  d2->fill) 
    RETURN_ERROR("invalid time", "DateDiff", false);

  *diff = d1->jday2000 - d2->jday2000;
  *diff += (d1->sod - d2->sod) / 86400.0;

  return true;
}

bool DateCopy(Date_t *this, Date_t *copy) {

  if (this == (Date_t *)NULL  ||  copy == (Date_t *)NULL) 
    RETURN_ERROR("invalid date structure", "DateCopy", false);

  copy->fill = this->fill;
  copy->year = this->year;
  copy->doy = this->doy;
  copy->month = this->month;
  copy->day = this->day;
  copy->hour = this->hour;
  copy->minute = this->minute;
  copy->second = this->second;
  copy->jday2000 = this->jday2000;
  copy->sod = this->sod;

  return true;
}

bool FormatDate(Date_t *this, Date_format_t iformat, char *s) {

  if (this == (Date_t *)NULL)
    RETURN_ERROR("invalid date structure", "FormatDate", false);

  if (iformat == DATE_FORMAT_DATEA_TIME) {
    if (sprintf(s, "%4d-%02d-%02dT%02d:%02d:%09.6fZ", 
                this->year, this->month, this->day, 
		this->hour, this->minute, this->second) < 0) 
      RETURN_ERROR("formating date/time", "FormatDate", false);
  } else if (iformat == DATE_FORMAT_DATEB_TIME) {
    if (sprintf(s, "%4d-%03dT%02d:%02d:%09.6fZ", 
                this->year, this->doy, 
		this->hour, this->minute, this->second) < 0) 
      RETURN_ERROR("formating date/time", "FormatDate", false);
  } else if (iformat == DATE_FORMAT_DATEA) {
    if (sprintf(s, "%4d-%02d-%02d", 
                this->year, this->month, this->day) < 0) 
      RETURN_ERROR("formating date", "FormatDate", false);
  } else if (iformat == DATE_FORMAT_DATEB) {
    if (sprintf(s, "%4d-%03d", 
                this->year, this->doy) < 0) 
      RETURN_ERROR("formating date", "FormatDate", false);
  } else if (iformat == DATE_FORMAT_TIME) {
    if (sprintf(s, "%02d:%02d:%09.6f", 
                this->hour, this->minute, this->second) < 0) 
      RETURN_ERROR("formating time", "FormatDate", false);
  } else 
    RETURN_ERROR("invalid format paramter", "FormatDate", false);
  
  return true;
}

short getdoy(short year,short month,short day)
{
    int dayatmonth[]=
        {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
    int jdate;

    jdate = day + dayatmonth[month];
    if (!LEAPYR(year) && month >= MARCH)
        jdate--;

    return(jdate);
}

int getdaymonth(short year,short doy,short *month,short *day)
{
    int i;
    int daysinmonth[]= {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    short totaldays=0;

    if (LEAPYR(year))
        daysinmonth[2]=29;

    for (i=1;(i<=12)&&(totaldays < doy);i++)
        totaldays += daysinmonth[i];

    if ((totaldays < doy)||(doy<=0))
        return -1;

    totaldays -= daysinmonth[i-1];
    *month=(short)i-1;
    *day=doy-totaldays;

    return(0);
}

