/***************************************************************/
/*                  Calendar Program                           */
/*    This program opens a window, reads the system date and   */
/*    displays the current month calendar and date.  The mouse */
/*    may be used to change the date.  Saves date on Exit      */
/***************************************************************/

#include <exec/types.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <exec/libraries.h>
#include <devices/timer.h>
#include <functions.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>

#define INTUITION_REV 33L
#define GRAPHICS_REV  33L
#define MAXLIN 	      80

char *mn[12] = {"January","February","March","April","May","June","July",
		"August","September","October","November","December"};
short nday[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
char *Curr_Year = "1984";
char *Time = "12:00";
char s[]="Date 12:00 07-Jul-86";
short CC,CR,da,wk,mo,yr,hr,min,sec;
USHORT pot[2];
short matrix[6][7];
static char *lines[6] = {"                    ",  /* init lines for calendar*/
	 		 "                    ",
			 "                    ",
			 "                    ",
			 "                    ",
			 "                    ",
			};

struct IntuitionBase  *IntuitionBase;
struct GfxBase 	      *GfxBase;
struct Window         *Win;
struct FileHandle     *Open();
struct Gadget	      Cg[8];
struct PropInfo	      Cinfo[2];
struct Image	      Cimage[2];

main(argc,argv)
int  argc;
char *argv[];
{
    struct IntuiMessage *message;
    struct Gadget 	*gad;  
    ULONG  class,code, x,y;
    BOOL   Quit,Save,Memo,GadFlag;
    USHORT ID;
    float  real;
	
    short dx,dy,wkdy,maxday;
    VOID  show_month(),Last_Time(),show_day(),FrameIt(),Point_Block();
    VOID  Reset_Date(),Show_Time(),Show_Help(),OpenAll(),Make_Prop_Gadget(),
	  Make_BOOL_Gadget(),Get_Hours(),Get_Minutes(),Tag(),Reminders(),
	  Set_Time();
    short day_of_week(),set_days(),Get_Month();
    long  Convert();

    Last_Time();OpenAll();FrameIt(Win);
    maxday=set_days(mo+1,yr);wkdy=day_of_week(mo,yr);
    show_month(da,mo,yr,maxday,wkdy);
    Show_Time(0);Point_Block(CC,CR);
    if (argc<2) OffGadget(&Cg[7],Win,NULL);

    for (Quit=FALSE,Save=TRUE,Memo=FALSE,GadFlag=FALSE;!Quit;) {
   	while (!(message=GetMsg(Win->UserPort))) WaitPort(Win->UserPort);
   	class = message->Class;    code = message->Code;
   	x     = message->MouseX;   y    = message->MouseY;
	gad   = (struct Gadget *)message->IAddress;
   	ReplyMsg( message );
   	switch ( class ) {
      	    case CLOSEWINDOW: Quit=TRUE;Save=FALSE;break;
	    case GADGETDOWN:  ID=gad->GadgetID;GadFlag=TRUE;
			      if (ID>5) {
				  Quit=TRUE;if (ID==7) Memo=TRUE;
				} else if (ID>1) {
				      switch(ID) {
					case 2: yr--;break;
					case 3: yr++;break;
					case 4: mo--;if (mo<0) { yr--;mo=11; }
					    if (da>nday[mo]) da=nday[mo];
					    break;
					case 5: mo++;if (mo>11) { yr++;mo=0; }
					    if (da>nday[mo]) da=nday[mo];
					    break;
			       	       }
		 	              maxday=set_days(mo+1,yr);
	       		      	      wkdy=day_of_week(mo,yr);
	       		      	      Point_Block(CC,CR);
	       		      	      show_month(da,mo,yr,maxday,wkdy);
	       		      	      Point_Block(CC,CR);
				    }
			       break;
      	    case MOUSEBUTTONS: if (code==SELECTDOWN && y>47 && y<121) {
	            dx=(x-6)/24;dy=(y-50)/10;
	            if (dx<0) dx=0;if (dx>6) dx=6;
	            if (dy<0) dy=0;if (dy>5) dy=5;
	            if (matrix[dy][dx]) {
	            	Point_Block(CC,CR);Point_Block(dx,dy);
		    	CC=dx;CR=dy;da=matrix[dy][dx];
	              }
		    GadFlag=FALSE;
         	  }
         	break;
      	    case MOUSEMOVE:if (ID<2 && GadFlag && Cinfo[ID].HorizPot!=pot[ID]){
		    real=(Cinfo[ID].HorizPot+1.0)/65536.0;
		    if (ID==0) hr=real*24; else min=real*60;
		    pot[ID]=Cinfo[ID].HorizPot;Show_Time();
		 }
      	  }
      }
    CloseWindow(Win);
    if (yr>1978 && Save) {
  	Reset_Date();Set_Time(Convert());Tag();
	if (Memo && argc==2) Reminders(argc,argv);
      }
}


VOID Tag()		/* Write File with current date to S: */
{
    struct FileHandle   *out;

    if ((out=Open("S:now",1006L)) != NULL) {
	Write(out,&s,(long)strlen(s));Close(out);
      } else printf("Can't update S:now !!!\n");
}

VOID Reminders(num,arg)		/* Display Reminders */
int num;
char *arg[];
{
    FILE   *in;
    char   line[MAXLIN];
    short  min,max,day,month,time,cnt,sw,a1,a2;

	if (da<6) min=(mo-1)*100+(nday[mo-1]+da-5);
	    else min=mo*100+da-5;
	if ((da+10)>nday[mo]) max=(mo+1)*100+(da+10-nday[mo]);
	    else max=mo*100+da+10;
	if (min<1) min=1;
	if (max>1131) max=1131;sw=1;
	if ((in=fopen(arg[1],"r")) != NULL) {
	    while (fgets(line,MAXLIN,in) != NULL) {
		day=line[0]-48;
		if (day<1) day=0;
		if (line[1] != 32) {
		    day=day*10+line[1]-48;
		    cnt=3;
		  } else cnt=2;
		month=Get_Month(line[cnt],line[cnt+1],line[cnt+2])-1;
		time=month*100+day;
		if ((time>=min) && (time<=max)) {
		    if (sw) {
			printf("\n");
			printf("%2d %-13sReminders\n",da,mn[mo]);
			printf("-----------------------------------\n");
			sw=0;
		      }
		    printf(line);
		  }
	      }
	    fclose(in);
	  } else printf("%s file not available...\n",arg[1]);
    printf("\n");
}

short Get_Month(a,b,c)  	/* Convert Alpha Month to Number */
short a,b,c;
{
    short val;

    if (a>96) a-=32;
    if (b>96) b-=32;
    if (c>96) c-=32;
    switch (a) {
	case 65: if (b==80) val=4; else val=8;
		 break;
	case 68: val=12;break;
	case 70: val=2;break;
	case 74: if (b==65) val=1;
		     else if (c==78) val=6; else val=7;
		 break;
	case 77: if (c==82) val=3; else val=5;
		 break;
	case 78: val=11;break;
	case 79: val=10;break;
	case 83: val=9;break;
	}
    return val;
}

VOID OpenAll()		/* Open everything */
{
    struct NewWindow nw;
    USHORT i;
    float  real;

    if (!(IntuitionBase=(struct IntuitionBase *)
	OpenLibrary("intuition.library",INTUITION_REV))) {
	    printf("Intuition Open failed.\n");
  	    exit(FALSE);
          }
    if (!(GfxBase=(struct GfxBase *)
	OpenLibrary("graphics.library",GRAPHICS_REV))) {
	    printf ("graphics open failed.\n");
	    exit(FALSE);
          }
    nw.LeftEdge=232;		nw.TopEdge=12;	    	nw.Width=178;
    nw.Height=144;		nw.DetailPen=0x00;  	nw.BlockPen=0x01;
    nw.Title=(UBYTE *)" Calendar ";
    nw.Flags=ACTIVATE|SMART_REFRESH|WINDOWCLOSE|WINDOWDRAG|
		WINDOWDEPTH|REPORTMOUSE;
    nw.IDCMPFlags=CLOSEWINDOW|MOUSEBUTTONS|MOUSEMOVE|GADGETDOWN|GADGETUP;
    nw.Type=WBENCHSCREEN;	nw.FirstGadget=&Cg[0];	nw.CheckMark=NULL;
    nw.Screen=NULL;		nw.BitMap=NULL;     	nw.MinWidth=100;
    nw.MinHeight=60;		nw.MaxWidth=640;    	nw.MaxHeight=200;

    real=(float)hr/24.0;pot[0]=real*65536+1;
    real=(float)min/60.0;pot[1]=real*65536+1;

    Make_Prop_Gadget(&Cg[0],8,120,80,7,&Cinfo[0],&Cimage[0],pot[0]);
    Cg[0].NextGadget=&Cg[1];
    Make_Prop_Gadget(&Cg[1],90,120,80,7,&Cinfo[1],&Cimage[1],pot[1]);
    Cg[1].NextGadget=&Cg[2];
    Make_BOOL_Gadget(&Cg[2],9,14,48,8);Cg[2].NextGadget=&Cg[3];
    Make_BOOL_Gadget(&Cg[3],120,14,48,8);Cg[3].NextGadget=&Cg[4];
    Make_BOOL_Gadget(&Cg[4],9,24,48,8);Cg[4].NextGadget=&Cg[5];
    Make_BOOL_Gadget(&Cg[5],120,24,48,8);Cg[5].NextGadget=&Cg[6];
    Make_BOOL_Gadget(&Cg[6],9,130,76,8);Cg[6].NextGadget=&Cg[7];
    Make_BOOL_Gadget(&Cg[7],93,130,76,8);
    for (i=0;i<8;i++) Cg[i].GadgetID=i;
    Win=(struct Window *)(OpenWindow(&nw));
}

VOID Make_Prop_Gadget(g,l,t,w,h,info,image,pot)
struct Gadget *g;
USHORT l,t,w,h;
USHORT pot;
struct Image    *image;
struct PropInfo *info;
{
    info->Flags=AUTOKNOB|FREEHORIZ;
    info->VertPot=0L;		info->HorizPot=(ULONG)pot;
    info->HorizBody=1L;		info->VertBody=1L;
    g->NextGadget=NULL;	        g->LeftEdge=l;         g->TopEdge=t;
    g->Width=w;			g->Height=h;
    g->Flags=GADGHCOMP|SELECTED;
    g->Activation=GADGIMMEDIATE|FOLLOWMOUSE|RELVERIFY;
    g->GadgetType=PROPGADGET;   g->GadgetRender=(APTR)image;
    g->SelectRender=NULL;	g->GadgetText=NULL;  g->MutualExclude=NULL;
    g->SpecialInfo=(APTR)info;  g->GadgetID=NULL;    g->UserData=NULL;
}

VOID Make_BOOL_Gadget(g,l,t,w,h)
struct Gadget *g;
USHORT  l,t,w,h;
{
    g->NextGadget=NULL;	        g->LeftEdge=l;         g->TopEdge=t;
    g->Width=w;			g->Height=h;
    g->Flags=GADGHCOMP;
    g->Activation=GADGIMMEDIATE;
    g->GadgetType=BOOLGADGET;   g->GadgetRender=NULL;
    g->SelectRender=NULL;	g->GadgetText=NULL;  g->MutualExclude=NULL;
    g->SpecialInfo=NULL;        g->GadgetID=NULL;    g->UserData=NULL;
}

void Reset_Date()		/* Reset Date String to Current Date */
{
    hr %=24;
    s[5]=(hr/10)+48;
    s[6]=(hr%10)+48;
    s[8]=(min/10)+48;
    s[9]=(min%10)+48;
    s[11]=(da/10)+48;
    s[12]=(da%10)+48;
    s[14]=mn[mo][0];
    s[15]=mn[mo][1];
    s[16]=mn[mo][2];
    s[18]=((yr%100)/10)+48;
    s[19]=(yr%10)+48;
}

void Show_Time(x)		/* Display Time */
short x;
{
  short h,i,morn;

    if (min>59) min=min-60;
    if (min<0) min=60+min;
    if (hr>24) hr=hr-24;
    if (hr<1) hr=24+hr;
    if (hr>12) h=hr-12; else h=hr;
    Time[0]=(h/10)+48;
    Time[1]=(h%10)+48;
    Time[3]=(min/10)+48;
    Time[4]=(min%10)+48;
    Move(Win->RPort,69L,118L);
    Text(Win->RPort,Time,5L);
    Move(Win->RPort,118L,118L);
    SetAPen(Win->RPort,3L); 
    if ((hr>11) && (hr<24)) Text(Win->RPort,"PM",2L);
      else Text(Win->RPort,"AM",2L);
    SetAPen(Win->RPort,1L);   
}

void Point_Block(nx,ny)		/* Draw Cursor Square around Day */
SHORT nx,ny;
{
   LONG x,y;

    x=nx*24+7;y=ny*10+58;
    SetDrMd(Win->RPort,COMPLEMENT);
    Move(Win->RPort,x+1,y);
    Draw(Win->RPort,x+19,y);
    Draw(Win->RPort,x+19,y-10);
    Draw(Win->RPort,x,y-10);
    Draw(Win->RPort,x,y);
    SetDrMd(Win->RPort,JAM2);
}

void FrameIt(w)			/* Draw Frame for Calendar Window */
struct Window *w;
{   
    short i;

    SetDrMd(Win->RPort,JAM2);
    SetAPen(Win->RPort,3L);
    Move(w->RPort,5L,12L);
    Draw(w->RPort,172L,12L);
    Draw(w->RPort,172L,141L);
    Draw(w->RPort,5L,141L);
    Draw(w->RPort,5L,12L);
    Move(w->RPort,5L,35L);
    Draw(w->RPort,172L,35L);
    Move(w->RPort,5L,109L);
    Draw(w->RPort,172L,109L);
    Move(w->RPort,5L,128L);
    Draw(w->RPort,172L,128L);
    Move(w->RPort,89L,128L);
    Draw(w->RPort,89L,141L);
    SetAPen(Win->RPort,1L);
    RectFill(Win->RPort,9L,130L,85L,139L);
    RectFill(Win->RPort,93L,130L,168L,139L);
    Move(Win->RPort,22L,118L);Text(Win->RPort,"Time",4L);
    SetAPen(Win->RPort,2L);
    Move(Win->RPort,9L,32L);
    Text(Win->RPort," <<<<          >>>> ",20L);
    Move(Win->RPort,9L,22L);
    Text(Win->RPort," <<<<          >>>> ",20L);
    SetBPen(Win->RPort,1L);
    Move(Win->RPort,15L,137L);Text(Win->RPort,"Set Date",8L);
    Move(Win->RPort,98L,137L);Text(Win->RPort,"Memo/Set",8L);
    SetBPen(Win->RPort,0L);
}

void Last_Time()		/* Read the System Date and Time */
{

    struct tm *localtime(),*tstruct;
    long   tloc,time(),t;
    short  i;

    tloc=time();
    tstruct=localtime(&tloc);
    mo=tstruct->tm_mon;
    yr=tstruct->tm_year+1900;
    da=tstruct->tm_yday;
    wk=tstruct->tm_wday;
    hr=tstruct->tm_hour;
    min=tstruct->tm_min;
    sec=tstruct->tm_sec;
    for (i=0;i<mo;i++) da -= nday[i];
    if (da<1) {
	mo-=1;da=nday[mo];
      } 
}

short day_of_week(mo,yr)	/* Calculate the Day of the Week */
short mo,yr;
{
    float r;
    short i,j,k,l;

	r=365.0*yr+1+31.0*mo;mo=mo+1;
	if (mo>2) {
	    i=0.4*mo+2.3;j=yr/4;k=yr/100;l=0.75*(k+1);
	    r=r-i+j-l;
	} else {
	    i=(yr-1)/4;j=(yr-1)/100;k=0.75*(j+1);
	    r=r+i-k;
	  }
	r=r-273750.0;
	j=(-1.0*r)/7.0;i=r+j*7.0;
	if(i==7) i=0;
	return i;
}

void show_month(da,mo,yr,mxday,wkday)  /* Display Day, Month, Year */
short da,mo,yr,mxday,wkday;
{
    short cnt,row,i,j,x,y;

	for (i=0;i<6;i++) {
	  for (j=0;j<7;j++) {
	    matrix[i][j]=0;
	    lines[i][j*3]=32;
	    lines[i][j*3+1]=32;
	   }
	 }
        Move(Win->RPort,49L,32L);
        Text(Win->RPort,"          ",10L);
	SetAPen(Win->RPort,1L);
	i=(178-strlen(mn[mo])*8)/2;
	Curr_Year[0]=(yr/1000)+48;
	Curr_Year[1]=((yr%1000)/100)+48;
	Curr_Year[2]=((yr%100)/10)+48;
	Curr_Year[3]=yr%10+48;
	Move(Win->RPort,73L,22L);
	Text(Win->RPort,Curr_Year,4L);
	Move(Win->RPort,(long)i,32L);
	Text(Win->RPort,mn[mo],(long)strlen(mn[mo]));
	Move(Win->RPort,9L,46L);
	SetAPen(Win->RPort,3L);
	Text(Win->RPort," S  M  T  W  T  F  S",20L);
	cnt=wkday;row=0;
	for (i=1;i<(mxday+1);i++) {
	    if (i==da) {
		CR=row;CC=cnt;
	    }
	    if (i>9) lines[row][cnt*3]=(i/10)+48;
	        else lines[row][cnt*3]=32;
	    lines[row][cnt*3+1]=(i%10)+48;
	    matrix[row][cnt]=i;	
	    cnt++;
	    if (cnt==7 && i<mxday) {
	        cnt=0;row++;
	    }
	}
	SetAPen(Win->RPort,1L);
	for (i=0;i<6;i++) {
	    Move(Win->RPort,9L,(long)(i*10+56));
	    Text(Win->RPort,lines[i],20L);
	}
}

short set_days(mo,yr)    /* Check for Leap Year */
short mo,yr;
{
   short norm;

	if (mo==1 || mo==3 || mo==5 || mo==7 || mo==8 || mo==10 || mo==12)
	    return 31;
	  else if (mo==4 || mo==6 || mo==9 || mo==11)
	      return 30;
	    else {
	  	norm = 1;
	  	if ((yr % 4) == 0) norm = 0;
	  	if ((yr % 100) == 0) norm = 1;
	  	if ((yr % 400) == 0) norm = 0;
	  	if (norm) return 28;
		    else return 29;
	      }
}

VOID  Set_Time(secs)     /* Set System Clock */
long secs;
{
    struct timerequest *time_req;
    struct timerequest *CreateExtIO();
    struct MsgPort     *timer_port;
    long   error;
             
    timer_port = CreatePort( 0L, 0L );
    if ( ! timer_port ) exit( 1L );
    time_req=CreateExtIO(timer_port,(long)sizeof(struct timerequest));
    if (time_req==NULL) exit(1L);
    error =  OpenDevice (TIMERNAME,UNIT_MICROHZ,time_req,0L);
    if( error ) {
    	DeleteExtIO(time_req,(long)sizeof(struct timerequest));
        DeletePort( timer_port );
	exit( 2L );
      }
    time_req -> tr_node.io_Message . mn_ReplyPort = timer_port;
    time_req -> tr_node.io_Command = TR_SETSYSTIME;
    time_req -> tr_time.tv_secs    = secs;
    time_req -> tr_time.tv_micro   = 0L;
    DoIO( time_req );
    CloseDevice( time_req );
    DeleteExtIO(time_req,(long)sizeof(struct timerequest)); 
    DeletePort( timer_port );
 }

long Convert()		/* Convert Time to Seconds */
{
    long secs,years,days;
    short i;

    years=yr-1978;
    if (years<0) return(0L);
    for (i=0,days=0;i<mo;i++) days+=nday[i];
    days+=da-1;
    if (yr>1979) {
	days+=((yr-1980)/4)+1;
	if (((yr-1980)%4)==0 && mo<2) days--;
      }
    secs=(years*365+days)*86400+(long)hr*3600+min*60+sec;
    return(secs);
} 
