/*
  parse_date_time(char *str, DateTime *date_time);

  Input: pointer to parsed string beginning with "[Date_Time]".
  Return: 0 if successful, -1 if invalid

  Uses parse_time_range() to set times
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>
#include "dm.h"
#include "functionlib.h"

//void	clear_dt();
void clear_dt( DateTime *dt);
//void	inc_date();
void inc_date( DateTime *dt, int inc);
//int	same_date();
int same_date(DateTime *d1, DateTime *d2);
//void	set_dow();
void set_dow(DateTime *dt);
//int	to_julian();
int to_julian(DateTime *dt);
//void	to_date();
void to_date(int jd, DateTime *dt);

//char *parse_int();
//char *parse_int(char *tree, int *value);
//in functionlib.h

//char *parse_offset();
char *parse_offset(char *p, int *offset);

/*typedef struct date_time {
	int	month;
	int	day;
	int	year;
	int	dow;
	int	start_time;
	int	end_time;
} DateTime;
*/

static	DateTime	today,
			ref_date;

static char *days[] = {
	"SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
	"THURSDAY", "FRIDAY", "SATURDAY"
};

static char *months[] = {
	"JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
	"JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
};

static char *day_of_mon[] = {
	"FIRST", "SECOND", "THIRD", "FOURTH", "FIFTH", "SIXTH",
	"SEVENTH", "EIGHTH", "NINTH", "TENTH", "ELEVENTH", "TWELFTH",
	"THIRTEENTH", "FOURTEENTH", "FIFTEENTH", "SIXTEENTH", "SEVENTEENTH",
	"EIGHTEENTH", "NINETEENTH", "TWENTIETH",
	"TWENTY FIRST", "TWENTY SECOND", "TWENTY THIRD", "TWENTY FOURTH",
	"TWENTY FIFTH", "TWENTY SIXTH", "TWENTY SEVENTH", "TWENTY EIGHTH",
	"TWENTY NINTH", "THIRTIETH", "THIRTY FIRST"
};

int parse_date_time(char *tree, DateTime *dt)
{
    char	*p, *t;
    int	i;
    int	date_relative;

    clear_dt(dt);
    date_relative= 0;

    p= tree;
    /* scan for [ */
    if ( !(p= strchr(p, '[')) ) return(-1);

    for( ; *p;  ) {

	if( !scmp("[Day_Name]", p) ) {
    	    if( !(p= strchr(p, '.')) ) break;
	    p++;
	    for( i=0; i < 7; i++) { if(!scmp(days[i],p)) break; }
	    if( i < 7 )  dt->dow= i;
	}

	else if( !scmp("[Month_Name]", p) ) {
    	    if( !(p= strchr(p, '.')) ) break;
	    p++;
	    for( i=0; i < 12; i++) { if(!scmp(months[i],p)) break; }
	    if( i < 12 ) dt->month= i+1;
	}

	else if( !scmp("[Day_Number]", p) ) {
	    t= parse_int(p, &(dt->day));
    	    if( !(p= strchr(p, '.')) ) break;
	    p++;
	}

	else if( !scmp("[Day_Ordinal]", p) ) {
	    t= parse_int(p, &(dt->day));
    	    if( !(p= strchr(p, '.')) ) break;
	    p++;
	}

	else if( !scmp("[Year]", p) ) {
    	    if( !(p= strchr(p, '.')) ) break;
	    p++;
	    if( !scmp("NINETEEN NINETY NINE", p)) { dt->year= 1999; }
	    else if( !scmp("TWO THOUSAND", p)) { dt->year= 2000; }
	}

	else if( !scmp("[Today_Relative]", p) ) {
    	    if( !(p= strchr(p, '.')) ) break;
	    p++;
	    *dt= today;
	    dt->start_time= dt->end_time= -1;
	    if(!scmp("TODAY",p)) { }
	    else if(!scmp("TONIGHT",p)) {
		dt->start_time= 2100;
		dt->end_time= 2359;
	    }
	    else if(!scmp("THIS",p)) { }
	    else if(!scmp("TOMORROW",p)) {
		inc_date(dt, 1);
	    }
	}

	if( !scmp("[Date_Relative]", p) ) {
    	    if( !(p= strchr(p, '.')) ) break;
	    p++;

	    if( !scmp("[Day_Name]", p) ) {
    	        if( !(p= strchr(p, '.')) ) break;
	        p++;
		for( i=0; i < 7; i++) { if(!scmp(days[i],p)) break; }
		if( i < 7 )  dt->dow= i;
	    }

	    else if( !scmp("[date_offset]", p) ) {
		char *t;
		t= parse_offset(p, &date_relative);
    	        if( !(p= strchr(p, '.')) ) break;
	        p++;
	    }

	    if( *p == '[' ) p++;
	    if( *p == '_' ) p++;
	    else if( !scmp("next", p) ) { date_relative= 1; }
	    else if( !scmp("same", p) ) {
 		dt->day= ref_date.day;
        	dt->month= ref_date.month;
        	dt->year= ref_date.year;
        	dt->dow= ref_date.dow;
 	    }
	    else if( !scmp("previous", p) ) { date_relative= -1; }
	}

	/* scan for start of next token */
	if( *p == '[' ) p++;
        if ( !(p= strchr(p, '[')) ) break;
    }

    p= tree;
    i= parse_time_range(p, &(dt->start_time), &(dt->end_time));

    if( date_relative > 0 ) {
	dt->day= ref_date.day;
	dt->month= ref_date.month;
	dt->year= ref_date.year;
   	if(dt->dow >= 0) {
	    int dif;

	    dif= dt->dow - ref_date.dow;
	    if( dif < 0 ) dif += 7;
	    if( same_date(&ref_date, &today ) ) dif += 7;
	    inc_date(dt, dif);
	}
	else {
	    inc_date(dt, date_relative);
	}
    }
    /* if day name but no date */
    else if( (dt->dow >= 0) && (dt->day < 0) ) {
	    int dif;

	    dif= dt->dow - today.dow;
	    if( dif < 0 ) dif += 7;
	    dt->day= today.day;
	    dt->month= today.month;
	    dt->year= today.year;
	    inc_date(dt, dif);
    }

    if( (dt->month < 0) && (dt->day <0) && (dt->dow < 0)) return(-1);

    if( dt->month < 0 ) dt->month= ref_date.month;
    if( dt->year < 0 ) dt->year= ref_date.year;
    if( dt->dow < 0 ) set_dow(dt);
    return(0);
}

void clear_dt( DateTime *dt)
{
    dt->month= -1;
    dt->day= -1;
    dt->year= -1;
    dt->dow= -1;
    dt->start_time= -1;
    dt->end_time= -1;
}

void set_today(DateTime *dt)
{
    time_t now= time(0);
    struct tm *tp;

    tp=	localtime(&now);
    today.month=	tp->tm_mon+1;  /* start month numbers at 1, not 0 */
    today.day=		tp->tm_mday;
    today.year=	tp->tm_year + 1900;
    today.dow=		tp->tm_wday;		/* 0-6 */
    *dt= today;
}

void set_ref_date(DateTime *dt)
{
    ref_date= *dt;
}

void inc_date( DateTime *dt, int inc)
{
    int	jd;

    jd= to_julian(dt);
    jd += inc;
    to_date(jd, dt);
    dt->dow= (jd+1) % 7;

}


int to_julian(DateTime *dt)
{
    int	jd,
	jy,
	jm,
	jg;

    if( dt->year <= 0 ) return(-1);
    if( dt->month > 2 ) {
	jy= dt->year;
	jm= dt->month +1;
    }
    else {
	jy= dt->year -1;
	jm= dt->month +13;
    }
    jd= (int)(365.25 * jy) + (int)(30.6001 * jm) + dt->day + 1720995;
    jg= (int)(0.01 * jy);
    jd += 2 - jg + (int)(0.25 * jg);
    return(jd);
}


void to_date(int jd, DateTime *dt)
{
    int jg,
	ja,
	jb, jc, jx, je;

    jg= (int)( ((jd - 1867216) - 0.25) / 36524.25);
    ja= jd +1 +jg - (int)(0.25 * jg);

    jb= ja + 1524;
    jc= (int)(6680.0 + ((jb-2439870) - 122.1)/365.25);
    jx= 365 * jc + (int)(0.25 * jc);
    je= (int)((jb-jx)/30.6001);
    dt->day= jb -jx - (int)(30.6001 * je);
    dt->month= je -1;
    if(dt->month > 12) dt->month -= 12;
    dt->year= jc - 4715;
    if(dt->month > 2) dt->year--;
}

void set_dow(DateTime *dt)
{
    int	jd;

    jd= to_julian(dt);
    dt->dow= (jd+1) % 7;
}

int same_date(DateTime *d1, DateTime *d2)
{
    if( d1->day != d2->day ) return(0);
    if( d1->month != d2->month ) return(0);
    if( d1->year != d2->year ) return(0);
    return(1);
}

char *parse_offset(char *p, int *offset)
{
    int paren;
    int count;

    count= 1;
    paren= 0;
    while( *p && (*p != '(') ) p++;
    if(!*p) return(p);
    paren++;
    for(p++; *p && paren; p++) {
	switch(*p) {
	case '(':	paren++;
			break;
	case ')':	paren--;
			break;
	case '[':
			if( !scmp("[Number]", p) ) {
			    char *t;
			    t= parse_int(p, &count);
			    if(t) p=t;
			}
	}
    }
    *offset= count;
    return(p);
}


void print_date( char *str, char *mon, char *dy)
{
    int month, day;

    if( !mon || !dy ) { *str= (char)0; return;}

    month= atoi(mon);
    day= atoi(dy);
    if( (month < 1) || (day < 1) ) return;

    strcpy(str, months[month-1]);
    strcat(str, " ");
    strcat(str, day_of_mon[day-1]);
}
