#!/usr/local/bin/pike
#pragma strict_types

typedef int(1..12) Month;
typedef int(1..31) Day;
typedef int(1..) Year;

constant weekdays = ({ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" });

string month_name( Month month )
{
	return ({ "January", "February", "March", "April", "May", "June",
            "July", "August", "September", "October", "November", "December" })[month-1];
}

int(28..31) month_length( Month month, Year year )
{
    switch (month)
    {
        case 1: case 3: case 5: case 7: case 8: case 10: case 12:
            return 31;
        case 4: case 6: case 9: case 11:
            return 30;
        case 2:
            if( (year % 4) || (!(year % 100) && ((year/100) % 4)) )
                return 28;
            else
                return 29;
    }
}

int(0..6) weekday( Day day, Month month, Year year )
{
    int days = 365 * ( year - 1 ) + ( year - 1 ) / 4 - ( year - 1 ) / 100  + ( year - 1 ) / 400;  // from full years
    for ( Month m = 1; m < month; m++ ) // complete months of current year
        days += month_length( m, year );
    days += day;
    return  [int(0..6)](days % 7);
}

array(array(Day)) cal_month( Month month, Year year )
{
    int(0..6) k = weekday( 1, month, year );
    array(array(Day)) cal_table;
    array(Day) cal_row = [array(Day)]( ({ 0 }) * k +
                                enumerate( month_length( month, year ), 1, 1 ) );
    cal_table = cal_row / 7.0;
    if ( sizeof( cal_table[-1] ) != 7 )
        cal_table[-1] += ( ({ 0 }) * ( 7 - (sizeof( cal_row ) % 7 ) ) );
    return cal_table;
}

string cal_txt( Month month, Year year )
{
    string res = sprintf ( "%|21s\n", month_name( month ) + " " + (string)year ) +
        sprintf("%{%-!2s %}\n", weekdays);
    foreach ((array(array(string)))cal_month( month, year), array(string) row)
    {
        row = [array(string)]map( row, lambda( string s ){ return (s=="0") ? "" : s; } );
        res += sprintf("%{%2s %}\n", row);
    }

    return res;
}

int main( int ac, array(string) av )
{
    Month month;
    Year year;

    switch(ac-1)
    {
    case 0:
        mapping m = localtime( time() );
        month = [int(1..12)](m->mon + 1);
        year = [int(1..)](m->year + 1900);
        break;
    case 1:
        array(string) a;
        for ( int k = 1; k <= 12 ; k++ )
        {
            a = av[0..0] + ({ (string)k }) + av[1..1];
            int r = main( 3, a );
            if ( r )
            return r;
        }
            return 0;
    case 2:
        month = [int(1..12)](int)(av[1]);
        year = [int(1..)](int)(av[2]);
        break;
    default:
        werror( "%s: wrong number of arguments (should be 1 or 2: [month] year)\n",
                basename(av[0]));
        return 1;
    }

    if ( ( month < 1 ) || ( month > 12 ) || ( year < 1 ) )
    {
        werror( "%s: unsupported arguments: month = %d, year = %d\n",
            basename(av[0]), month, year );
        return 1;
    }

    write( cal_txt( month, year ) );
    return 0;
}