/* Copyright (c) 1993 by Sanjay Ghemawat */
/*
 * Time/Date interface for Tcl.
 */

#include <stdio.h>
#include <string.h>

extern "C" {
#include <tcl.h>
}

#include "Date.h"
#include "Time.h"

#include "Month.h"
#include "WeekDay.h"
#include "Year.h"

#include "commands.h"
#include "misc_tcl.h"

/*
 * Tcl representations for time/date entities
 *
 *	Month    is an integer in range 1..12
 *	Monthday is an integer in range 1..31
 *	Weekday  is an integer in range 1..7 (1 == Sunday)
 *	Year     is an integer in some range (possibly 1900..2400)
 *	Date     is an integer since some unspecified date)
 *	Time     is a double in seconds since some unspecified date
 */

/*
 * Date command
 *
 *	date make <day> <month> <year>	-- returns date
 *	date today			-- returns date
 *	date first			-- returns date
 *	date last			-- returns date
 *	date monthsize <date>		-- returns integer
 *	date monthday <date>		-- returns monthday
 *	date weekday <date>		-- returns weekday
 *	date month <date>		-- returns month
 *	date year <date>		-- returns year
 *	date split <date>		-- returns monthday,weekday,month,year
 */
int Cmd_Date(ClientData, Tcl_Interp* tcl, int argc, char* argv[]) {
    char* cmd = argv[1];
    char buffer[100];
    int days;
    Date d;

    switch (argc) {
      case 2:
	if ((cmd[0] == 't') && (strcmp(cmd, "today") == 0)) {
	    sprintf(buffer, "%ld", Date::Today().EpochDays());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'f') && (strcmp(cmd, "first") == 0)) {
	    sprintf(buffer, "%ld", Date::First().EpochDays());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'l') && (strcmp(cmd, "last") == 0)) {
	    sprintf(buffer, "%ld", Date::Last().EpochDays());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	break;
      case 3:
	/* Argv[2] is date. */
	if (Tcl_GetInt(tcl, argv[2], &days) != TCL_OK) {
	    return TCL_ERROR;
	}
	d = Date(days);

	if ((cmd[0] == 'm') && (strcmp(cmd, "monthsize") == 0)) {
	    sprintf(buffer, "%d", d.GetMonth().Size(d.GetYear()));
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'm') && (strcmp(cmd, "monthday") == 0)) {
	    sprintf(buffer, "%d", d.GetMDay());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'w') && (strcmp(cmd, "weekday") == 0)) {
	    sprintf(buffer, "%d", d.GetWDay().Index());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'm') && (strcmp(cmd, "month") == 0)) {
	    sprintf(buffer, "%d", d.GetMonth().Index());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'y') && (strcmp(cmd, "year") == 0)) {
	    sprintf(buffer, "%d", d.GetYear());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 's') && (strcmp(cmd, "split") == 0)) {
	    int mday, year;
	    Month month;
	    WeekDay wday;

	    d.BreakDown(mday, wday, month, year);
	    sprintf(buffer, "%d %d %d %d",
		    mday,
		    wday.Index(),
		    month.Index(),
		    year);
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	break;
      case 5:
	if ((cmd[0] == 'm') && (strcmp(cmd, "make") == 0)) {
	    int month, year;

	    if ((Tcl_GetInt(tcl, argv[2], &days) != TCL_OK) ||
		(days < 1) ||
		(days > 31) ||
		(Tcl_GetInt(tcl, argv[3], &month) != TCL_OK) ||
		(month < 1) ||
		(month > 12) ||
		(Tcl_GetInt(tcl, argv[4], &year) != TCL_OK) ||
		(year < Year::First()) ||
		(year > Year::Last())) {
		TCL_Error(tcl, "illegal date specification");
	    }

	    d = Date(days, Month::First()+(month-1), year);
	    sprintf(buffer, "%ld", d.EpochDays());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	break;
      default:
	break;
    }

    TCL_Error(tcl, "invalid arguments to date");
}

/*
 * Time command
 *
 *	time now		-- returns time
 *	time date <time>	-- returns date
 *	time hour <time>	-- returns hour of the day
 *	time minute <time>	-- returns minute within hour
 *	time second <time>	-- returns seconds within minute
 *	time millisecond <time>	-- returns millisec within second
 *	time split <time>	-- returns hour,minute,second,millisecond
 */
int Cmd_Time(ClientData, Tcl_Interp* tcl, int argc, char* argv[]) {
    char* cmd = argv[1];
    char buffer[100];
    double secs;
    Time t;
    int hour, minute, second, millisecond;

    switch (argc) {
      case 2:
	if ((cmd[0] == 'n') && (strcmp(cmd, "now") == 0)) {
	    sprintf(buffer, "%lf", Time::Now().EpochSeconds());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	break;
      case 3:
	/* Argv[2] is time */
	if (Tcl_GetDouble(tcl, argv[2], &secs) != TCL_OK) {
	    return TCL_ERROR;
	}
	t = Time(secs);

	if ((cmd[0] == 'd') && (strcmp(cmd, "date") == 0)) {
	    int day, year;
	    Month month;
	    WeekDay wday;
	    t.BreakDownDate(day, wday, month, year);
	    Date date(day, month, year);

	    sprintf(buffer, "%ld", date.EpochDays());
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}

	t.BreakDownClock(hour, minute, second, millisecond);
	if ((cmd[0] == 'h') && (strcmp(cmd, "hour") == 0)) {
	    sprintf(buffer, "%d", hour);
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'm') && (strcmp(cmd, "minute") == 0)) {
	    sprintf(buffer, "%d", minute);
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 's') && (strcmp(cmd, "second") == 0)) {
	    sprintf(buffer, "%d", second);
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 'm') && (strcmp(cmd, "millisecond") == 0)) {
	    sprintf(buffer, "%d", millisecond);
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	if ((cmd[0] == 's') && (strcmp(cmd, "split") == 0)) {
	    sprintf(buffer, "%d %d %d %d",
		    hour,
		    minute,
		    second,
		    millisecond);
	    Tcl_SetResult(tcl, buffer, TCL_VOLATILE);
	    return TCL_OK;
	}
	break;
      default:
	break;
    }

    TCL_Error(tcl, "invalid arguments to time");
}
