#include <u.h>
#include <libc.h>
#include <bio.h>

char    dayw[] =
{
       " S  M Tu  W Th  F  S"
};
char    *smon[] =
{
       "January", "February", "March", "April",
       "May", "June", "July", "August",
       "September", "October", "November", "December",
};
char    mon[] =
{
       0,
       31, 29, 31, 30,
       31, 30, 31, 31,
       30, 31, 30, 31,
};
char    string[432];
Biobuf  bout;

void    main(int argc, char *argv[]);
int     number(char *str);
void    pstr(char *str, int n);
void    cal(int m, int y, char *p, int w);
int     jan1(int yr);
int     curmo(void);
int     curyr(void);

void
main(int argc, char *argv[])
{
       int y, i, j, m;

       if(argc > 3) {
               fprint(2, "usage: cal [month] [year]\n");
               exits("usage");
       }
       Binit(&bout, 1, OWRITE);

/*
* no arg, print current month
*/
       if(argc == 1) {
               m = curmo();
               y = curyr();
               goto xshort;
       }

/*
* one arg
*      if looks like a month, print month
*      else print year
*/
       if(argc == 2) {
               y = number(argv[1]);
               if(y < 0)
                       y = -y;
               if(y >= 1 && y <= 12) {
                       m = y;
                       y = curyr();
                       goto xshort;
               }
               goto xlong;
       }

/*
* two arg, month and year
*/
       m = number(argv[1]);
       if(m < 0)
               m = -m;
       y = number(argv[2]);
       goto xshort;

/*
*      print out just month
*/
xshort:
       if(m < 1 || m > 12)
               goto badarg;
       if(y < 1 || y > 9999)
               goto badarg;
       Bprint(&bout, "   %s %ud\n", smon[m-1], y);
       Bprint(&bout, "%s\n", dayw);
       cal(m, y, string, 24);
       for(i=0; i<6*24; i+=24)
               pstr(string+i, 24);
       exits(0);

/*
*      print out complete year
*/
xlong:
       y = number(argv[1]);
       if(y<1 || y>9999)
               goto badarg;
       Bprint(&bout, "\n\n\n");
       Bprint(&bout, "                                %ud\n", y);
       Bprint(&bout, "\n");
       for(i=0; i<12; i+=3) {
               for(j=0; j<6*72; j++)
                       string[j] = '\0';
               Bprint(&bout, "         %.3s", smon[i]);
               Bprint(&bout, "                    %.3s", smon[i+1]);
               Bprint(&bout, "                    %.3s\n", smon[i+2]);
               Bprint(&bout, "%s   %s   %s\n", dayw, dayw, dayw);
               cal(i+1, y, string, 72);
               cal(i+2, y, string+23, 72);
               cal(i+3, y, string+46, 72);
               for(j=0; j<6*72; j+=72)
                       pstr(string+j, 72);
       }
       Bprint(&bout, "\n\n\n");
       exits(0);

badarg:
       Bprint(&bout, "cal: bad argument\n");
}

struct
{
       char*   word;
       int     val;
} dict[] =
{
       "jan",          1,
       "january",      1,
       "feb",          2,
       "february",     2,
       "mar",          3,
       "march",        3,
       "apr",          4,
       "april",        4,
       "may",          5,
       "jun",          6,
       "june",         6,
       "jul",          7,
       "july",         7,
       "aug",          8,
       "august",       8,
       "sep",          9,
       "sept",         9,
       "september",    9,
       "oct",          10,
       "october",      10,
       "nov",          11,
       "november",     11,
       "dec",          12,
       "december",     12,
       0
};

/*
* convert to a number.
* if its a dictionary word,
* return negative  number
*/
int
number(char *str)
{
       int n, c;
       char *s;

       for(n=0; s=dict[n].word; n++)
               if(strcmp(s, str) == 0)
                       return -dict[n].val;
       n = 0;
       s = str;
       while(c = *s++) {
               if(c<'0' || c>'9')
                       return 0;
               n = n*10 + c-'0';
       }
       return n;
}

void
pstr(char *str, int n)
{
       int i;
       char *s;

       s = str;
       i = n;
       while(i--)
               if(*s++ == '\0')
                       s[-1] = ' ';
       i = n+1;
       while(i--)
               if(*--s != ' ')
                       break;
       s[1] = '\0';
       Bprint(&bout, "%s\n", str);
}

void
cal(int m, int y, char *p, int w)
{
       int d, i;
       char *s;

       s = p;
       d = jan1(y);
       mon[2] = 29;
       mon[9] = 30;

       switch((jan1(y+1)+7-d)%7) {

       /*
        *      non-leap year
        */
       case 1:
               mon[2] = 28;
               break;

       /*
        *      1752
        */
       default:
               mon[9] = 19;
               break;

       /*
        *      leap year
        */
       case 2:
               ;
       }
       for(i=1; i<m; i++)
               d += mon[i];
       d %= 7;
       s += 3*d;
       for(i=1; i<=mon[m]; i++) {
               if(i==3 && mon[m]==19) {
                       i += 11;
                       mon[m] += 11;
               }
               if(i > 9)
                       *s = i/10+'0';
               s++;
               *s++ = i%10+'0';
               s++;
               if(++d == 7) {
                       d = 0;
                       s = p+w;
                       p = s;
               }
       }
}

/*
*      return day of the week
*      of jan 1 of given year
*/
int
jan1(int yr)
{
       int y, d;

/*
*      normal gregorian calendar
*      one extra day per four years
*/

       y = yr;
       d = 4+y+(y+3)/4;

/*
*      julian calendar
*      regular gregorian
*      less three days per 400
*/

       if(y > 1800) {
               d -= (y-1701)/100;
               d += (y-1601)/400;
       }

/*
*      great calendar changeover instant
*/

       if(y > 1752)
               d += 3;

       return d%7;
}

/*
* system dependent
* get current month and year
*/
int
curmo(void)
{
       Tm *tm;

       tm = localtime(time(0));
       return tm->mon+1;
}

int
curyr(void)
{
       Tm *tm;

       tm = localtime(time(0));
       return tm->year+1900;
}