/* This may look like a -*- C -*- code, but it's only similiar.
 *
 *    Calc: A Programmable Calculator for GTK+
 *    Copyright (C) 2000  David Hanak (dhanak@inf.bme.hu)
 *
 *    This file can be distributed under GNU GPL.  See file COPYING.
 *
 *    $Id: age,v 2.1 2000/02/21 22:05:06 david Exp $
 * 
 * This is an example for Calc.
 */

// Calculation of someones age based on current date and birthday.

// Test whether arg is leap year.
fun is_leap_year(year) =
  year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);

// Returns length of given month in given year (might be leap year)
fun length_of_month(month, year) =
  if (month <  8 && month % 2 == 1 ||
      month >= 8 && month % 2 == 0)      31
  else if (month != 2)                   30
  else if (is_leap_year(year))           29
  else                                   28;

// Returns length of year
fun length_of_year(year) = if (is_leap_year(year)) 366 else 365;

// Splits a "MM.DD.YYYY." formed date to month, day and year variables
fun split_date(date) =
{
  fun split_off_value() =
  {
    var (dotpos, val);

    dotpos = strpos(date, ".");
    if (dotpos < 0) return -1;
    val = todbl(strsub(date, 0, dotpos-1));
    date = strsub(date, dotpos+1);

    val
  };

  month = split_off_value();
  day = split_off_value();
  if ((year = split_off_value()) == -1) year = todbl(date);

  0
};

// Tests whether month, day and year store a real date
fun is_date(month, day, year) =
  isdbl(month) && isdbl(day) && isdbl(year) &&
  month > 0 && month <= 12 &&
  day > 0   && day <= length_of_month(month, year);

// Returns length of the rest of the year, not counting given date
fun rest_of_year(month, day, year) =
{
  var length;

  if (!is_date(month, day, year))
    error("date expected");

  length = length_of_month(month, year) - day;
  for (month++, month <= 12, month++)
    length += length_of_month(month, year);

  length
};

// Returns the length of this year up to this date
fun year_until_now(month, day, year) =
{
  var length;

  if (!is_date(month, day, year))
    error("date expected");

  length = day;
  for (month--, month > 0, month--)
    length += length_of_month(month, year);

  length
};

// Number of elapsed days between two dates
fun elapsed_days(m1, d1, y1, m2, d2, y2) =
{
  var (days, y);

  if (!is_date(m1,d1,y1) || !is_date(m2,d2,y2))
    error("date expected");

  if (y1 > y2             ||
      y1 == y2 && m1 > m2 ||
      y1 == y2 && m1 == m2 && d1 > d2)
    days = -elapsed_days(m2,d2,y2,m1,d1,y1)
  else {
    days = rest_of_year(m1,d1,y1) + year_until_now(m2,d2,y2);
    if (y1 == y2) days -= length_of_year(y1)
    else for (y = y1+1, y < y2, y++) days += length_of_year(y)
  };

  days
};

// Print date to string
fun date2str(m,d,y) =
  strcat(switch (m) {
           case  1: "January"
           case  2: "February"
           case  3: "March"
           case  4: "April"
           case  5: "May"
           case  6: "June"
           case  7: "July"
           case  8: "August"
           case  9: "September"
           case 10: "October"
           case 11: "November"
           case 12: "December"
           default: error("unknown month")
         }, " ", d, "., ", y);

// This function does the actual processing. Call this!
fun my_age() =
{
  var (tmp, month, day, year, bm, bd, by, tm, td, ty);

  do {
    tmp = read("Your birthday (MM.DD.YYYY): ");
    split_date(tmp);
    bm = month; bd = day; by = year
  } while (!is_date(bm,bd,by) ||
	   !ask(strcat("Is your birthday really on ", date2str(bm,bd,by), "?")));

  do {
    tmp = read("Today is (MM.DD.YYYY): ");
    split_date(tmp);
    tm = month; td = day; ty = year
  } while (!is_date(tm,td,ty) ||
	   !ask(strcat("Is today really ", date2str(tm,td,ty), "?")));

  tmp = elapsed_days(bm,bd,by,tm,td,ty);
  msg(strcat("You are exactly ", tmp, " days old."));

  tmp
}
