
/*	AmyInfo v1.3 by Digital Design, Inc.

	Author: Juha Tuominen
	Released: 30-Jun-91		*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <libraries/dos.h>
#include <devices/timer.h>
#include <intuition/intuitionbase.h>
#include <graphics/rastport.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <graphics/text.h>
#include <proto/all.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define FONTSIZE 8
#define NAME "AmyInfo"
#define STACK_SIZE 1000L	/* Meisseli-Make's stack size */

BOOL	task_added=FALSE;
BOOL	timeropen=FALSE;
BOOL	keepgoing=TRUE;
BOOL	time_requested=FALSE;
BOOL	log=FALSE;
BOOL	init=TRUE;
char	*months[]={"","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
char	date[16]="               ";
char	tempdays[8]="       ";
char	timetemp[8]="       ";
char	booted[80];
int		upsecs=0,upmins=0,uphours=0,updays=0;
ULONG	maxchip, maxfast, freechip=0, freefast=0;
ULONG	cputime=NULL,cputemp=NULL,prevcpu=68;
ULONG	count=0;
ULONG	chipscale=0, fastscale=0;
ULONG	jakaja=0,cputemp2=0;
char	*taskname="Meisseli-Make";
APTR	stack=NULL;
USHORT	ditherdata=0x5555;
int		tempmins,cpuload=0;

extern	struct ExecBase	*SysBase;
struct	IntuitionBase *IntuitionBase=NULL;
struct	Window *window=NULL;
struct	GfxBase *GfxBase=NULL;
struct	MsgPort *timerport=NULL;
struct	timerequest *timermsg=NULL;
struct	IntuiText abouttext[];
struct	IntuiText proceed;
struct	Task *task=NULL;
struct	NewWindow mywindow = 
{
	100,
	100,
	400,
	76,
	0,1,
	CLOSEWINDOW | MOUSEBUTTONS,
	SMART_REFRESH | WINDOWDRAG | WINDOWCLOSE | WINDOWDEPTH | RMBTRAP,
	NULL,
	NULL,
	NAME,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	WBENCHSCREEN

};

struct TextAttr aifont[1] =
{
	{(UBYTE *)"topaz.font",8,FS_NORMAL ,FPF_ROMFONT},
};

struct IntuiText abouttext[7] =
{
	{2,1,JAM1,8, 4,(struct TextAttr *)&aifont[0],(UBYTE *)"AmyInfo v1.3",(struct IntuiText *)&abouttext[1]},
	{2,1,JAM1,8,14,(struct TextAttr *)&aifont[0],(UBYTE *)"by Juha Tuominen",(struct IntuiText *)&abouttext[2]},
	{2,1,JAM1,8,22,(struct TextAttr *)&aifont[0],(UBYTE *)"Copyright (c) 1991",(struct IntuiText *)&abouttext[3]},
	{2,1,JAM1,8,30,(struct TextAttr *)&aifont[0],(UBYTE *)"Digital Design, Inc.",(struct IntuiText *)&abouttext[4]},
	{2,1,JAM1,8,38,(struct TextAttr *)&aifont[0],(UBYTE *)" ",(struct IntuiText *)&abouttext[5]},
	{2,1,JAM1,8,46,(struct TextAttr *)&aifont[0],(UBYTE *)"Meisseli-Make has found",(struct IntuiText *)&abouttext[6]},
	{2,1,JAM1,8,54,(struct TextAttr *)&aifont[0],(UBYTE *)"0 perfect numbers.",(struct IntuiText *)NULL}
};

struct IntuiText proceed =
{
	2,1,JAM1,5,3,(struct TextAttr *)&aifont[0],(UBYTE *)"Continue",(struct IntuiText *)NULL
};



void cleanexit(int error);
void openthings(void);
void copyfile(char *source, char *dest);
void addtimerequest(long secs, long micros);
void currenttime(void);
void changefont(void);
void initgraphics(void);
void printfinetext(char *text,int x,int y,int color);
ULONG maxmemsize(long memtype);
void idletask(void);
void __saveds switchroutine(void);

void CXBRK(void) 
{ 
	cleanexit(0);
}

void cleanexit(int error)
{
	if(task_added) RemTask(task);
	if(task) FreeMem(task,sizeof(struct Task));
	if(stack) FreeMem(stack,STACK_SIZE);
	if(timeropen)
	{
		if(time_requested)
		{
			AbortIO((struct IORequest *)timermsg);
			WaitIO((struct IORequest *)timermsg);
		}
		CloseDevice((struct IORequest *)timermsg);
	}
	if(timermsg) DeleteExtIO((struct IORequest *)timermsg);
	if(timerport) DeletePort(timerport);
	if(window) CloseWindow(window);
	if(GfxBase) CloseLibrary(GfxBase);
	if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	exit(error);
}


void openthings(void)
{
	FILE	*fp;

	if(!DeviceProc("AmyInfo:") && log)
	{
		printf("\aAmyInfo: not assigned\n");
		cleanexit(10);
	}
	if(!DeviceProc("T:") && log)
	{
		printf("\aT: not assigned\n");
		cleanexit(10);
	}
	if (!(GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",33L)))
	{
		printf("\aError opening GfxBase\n");
		cleanexit(30);
	}
	if (!(IntuitionBase=(struct IntuitionBase*)OpenLibrary("intuition.library",33L)))
	{
		printf("\aError opening IntuitionBase\n");
		cleanexit(30);
	}
	if(!(timerport=CreatePort(0,0)))
	{
		printf("\aError creating timerport\n");
		cleanexit(22);
	}
	if(!(timermsg=(struct timerequest *) CreateExtIO(timerport, sizeof(struct timerequest))))
	{
		printf("\aError creating timerequest\n");
		cleanexit(22);
	}
	if(OpenDevice("timer.device", UNIT_VBLANK, ((struct IORequest *) timermsg), 0))
	{
		printf("\aError opening timer.device\n");
		cleanexit(22);
	}
	timeropen=TRUE;
	if(!(window=OpenWindow(&mywindow)))
	{
		printf("\aError opening window\n");
		cleanexit(27);
	}
	if(!(stack=AllocMem(STACK_SIZE, MEMF_CLEAR)))
	{
		printf("\aCannot allocate stack for idletask\n");
		cleanexit(10);
	}
	if(!(task=(struct Task*)AllocMem(sizeof(struct Task),MEMF_CLEAR|MEMF_PUBLIC)))
	{
		printf("\aCannot create idletask\n");
		cleanexit(10);
	}

	maxchip=maxmemsize(MEMF_CHIP);
	maxfast=maxmemsize(MEMF_FAST);

	/*	Because we run this part only once we just have to assume that user 
		does not add memory cartiges while this program is running :) */

	chipscale=maxchip/142;
	fastscale=maxfast/142;
	changefont();
	if(log)
	{
		currenttime();
		if(!(fp=fopen("AmyInfo:SystemLog","a")))
		{
			printf("\aUnable to open AmyInfo:SystemLog - check for protect bits!\n");
			cleanexit(10);
		}
		fputs(booted,fp);
		fclose(fp);
		copyfile("AmyInfo:SystemLog","T:TEMP.AmyInfo");
	}

}

void copyfile(char *source, char *dest)
{
	FILE	*fpsource,*fpdest;
	char	store[80]="AmyInfo!";
	
	if(!(fpsource=fopen(source,"r")))
		cleanexit(10);
	else
	{
		if(!(fpdest=fopen(dest,"w")))
			cleanexit(10);
		else
		{
			while(store[0]!=NULL)
			{
				fgets(store,80,fpsource);
				fputs(store,fpdest);
			}
			fclose(fpdest);
		}
		fclose(fpsource);
	}
}

void addtimerequest(long secs, long micros)
{
	time_requested=TRUE;
	timermsg->tr_node.io_Command=TR_ADDREQUEST;
	timermsg->tr_time.tv_secs=secs;
	timermsg->tr_time.tv_micro=micros;
	SendIO((struct IORequest *)timermsg);
}


void currenttime(void)
{
	long	n;
	int		m,d,y,hours,mins,secs;
	char	temp[16],uptemp[40];
	long	v[3];
	struct	DateStamp ds;
	FILE	*fp;

	DateStamp((struct DateStamp *)v);
	n=v[0]-2251;
	y=(4*n+3)/1461;
	n-=1461*(long)y/4;
	y+=84;
	m=(5*n+2)/153;
	d=n-(153*m+2)/5+1;
	m+=3;
	if (m>12)
	{
		y++;
		m-=12;
	}

/*	Date stamp conversation routine from Robert A. Peck's book 
	'Programming Guide to the Amiga. Routine by Tom Rokicki. */

	sprintf(temp,"%02d-%3s-%02d",d,months[m],y);
	if(strcmp(temp,date))
	{
		printfinetext(temp,70,38,2);
		strcpy(date,temp);
	}

	DateStamp(&ds);
	hours=ds.ds_Minute/60;
	mins=ds.ds_Minute%60;
	secs=ds.ds_Tick/50;	

	sprintf(temp,"%02d:%02d:%02d",hours,mins,secs);
	if(strcmp(timetemp,temp))
	{
		strcpy(timetemp,temp);
		printfinetext(temp,70,25,2);
		if(++upsecs>59)
		{
			upsecs=0;
			if(++upmins>59)
			{
				upmins=0;
				if(++uphours>23)
				{
					uphours=0;
					updays++;
				}
			}
		}		
		if(init)
		{
			sprintf(booted,"\nBooted: %s  %s\n",date,temp);
			tempmins=mins;
			init=FALSE;
		}
		sprintf(uptemp,"%02d:%02d:%02d",uphours,upmins,upsecs);
		printfinetext(uptemp,230,25,2);
	}

	sprintf(temp,"%04d",updays); /* I think 9999 days is enough for most of people :) */
	if(strcmp(temp,tempdays))
	{
		printfinetext(temp,230,38,2);
		strcpy(tempdays,temp);
	}
	if(tempmins-mins==45 || mins-tempmins==15)
	{
		tempmins=mins;
		copyfile("T:TEMP.AmyInfo","AmyInfo:SystemLog");
		if(!(fp=fopen("AmyInfo:SystemLog","a")))
			cleanexit(10);
		else
		{
			sprintf(booted,"Uptime: %s %s    Average CPU load: %d %%\n",temp,uptemp,100-cpuload);
			fputs(booted,fp);
			fclose(fp);
		}
	}
}

void changefont(void)
{
	struct	TextAttr	textattr;
	struct	TextFont	*textfont;

	textattr.ta_Name="topaz.font";
	textattr.ta_YSize=FONTSIZE;
	textattr.ta_Style=FS_NORMAL;
	textattr.ta_Flags=FPF_DESIGNED | FPF_ROMFONT;

	if(textfont=OpenFont(&textattr))
		SetFont(window->RPort,textfont);
	else
	{
		printf("Cannot open topaz 8!\n");
		cleanexit(10);
	}
}

void initgraphics(void)
{
	printfinetext("Time:",10,25,3);
	printfinetext("Date:",10,38,3);
	printfinetext("UpTime:",160,25,3);
	printfinetext("Days:",160,38,3);
	printfinetext("Chip:         K",10,51,3);
	printfinetext("Fast:          K",160,51,3);

	SetDrMd(window->RPort,JAM1);
	SetAPen(window->RPort,1);
	Move(window->RPort,310,69);
	Draw(window->RPort,310,18);
	Draw(window->RPort,390,18);
	Move(window->RPort,7,69);
	Draw(window->RPort,7,59);
	Draw(window->RPort,148,59);
	Move(window->RPort,152,69);
	Draw(window->RPort,152,59);
	Draw(window->RPort,303,59);
	SetAPen(window->RPort,2);
	Draw(window->RPort,303,69);
	Draw(window->RPort,153,69);
	Move(window->RPort,148,60);
	Draw(window->RPort,148,69);
	Draw(window->RPort,8,69);
	Move(window->RPort,390,19);
	Draw(window->RPort,390,69);
	Draw(window->RPort,310,69);	
}


ULONG maxmemsize(long memtype)
{
	ULONG blocksize=0;
	struct MemHeader *MemHeader;

	Forbid();
		for(MemHeader=(struct MemHeader *)SysBase->MemList.lh_Head;MemHeader->mh_Node.ln_Succ;MemHeader=(struct MemHeader *)MemHeader->mh_Node.ln_Succ)
		{
			if(MemHeader->mh_Attributes&memtype)
				blocksize+=((ULONG)MemHeader->mh_Upper-(ULONG)MemHeader->mh_Lower);
		}
	Permit();

	return(blocksize);

	/* MaxMemSize() by Louis A. Mamakos. */
}


void updatememorybar(void)
{
	ULONG currentchip, currentfast;
	long tempx;
	char temp[16];

	currentchip=AvailMem(MEMF_CHIP);
	currentfast=AvailMem(MEMF_FAST);


	if(currentchip!=freechip)
	{
		sprintf(temp,"%05d",currentchip/1024);
		printfinetext(temp,70,51,2);

		tempx=currentchip/chipscale;
		SetDrMd(window->RPort,JAM1);
		if(freechip>currentchip)
		{
			SetAPen(window->RPort,0);
			RectFill(window->RPort,tempx+10,61,freechip/chipscale+11,67);
		}
		SetAPen(window->RPort,3);
		RectFill(window->RPort,10,62,tempx+10,66);
		Move(window->RPort,tempx+11,61);
		SetAPen(window->RPort,1);
		Draw(window->RPort,tempx+11,67);
		Draw(window->RPort,9,67);
		SetAPen(window->RPort,2);
		Draw(window->RPort,9,61);
		Draw(window->RPort,tempx+11,61);
		freechip=currentchip;				
	}

	if(currentfast!=freefast)
	{
		sprintf(temp,"%05d",currentfast/1024);
		printfinetext(temp,230,51,2);

		tempx=currentfast/fastscale;
		SetDrMd(window->RPort,JAM1);
		if(freefast>currentfast)
		{
			SetAPen(window->RPort,0);
			RectFill(window->RPort,tempx+155,61,freefast/fastscale+156,67);
		}
		SetAPen(window->RPort,3);
		RectFill(window->RPort,155,62,tempx+155,66);
		Move(window->RPort,tempx+156,61);
		SetAPen(window->RPort,1);
		Draw(window->RPort,tempx+156,67);
		Draw(window->RPort,154,67);
		SetAPen(window->RPort,2);
		Draw(window->RPort,154,61);
		Draw(window->RPort,tempx+156,61);
		freefast=currentfast;				
	}
	/* scale cputime to usable form - we've got 50 pixels to fill */
	cputemp=(100*cputemp/60)/2;
	ScrollRaster(window->RPort,1,0,311,19,389,68);
	SetDrMd(window->RPort,JAM1);
	SetAPen(window->RPort,3);
	if(ditherdata==0x5555)
		ditherdata=0xaaaa;
	else
		ditherdata=0x5555;
	SetDrPt(window->RPort,ditherdata);
	Move(window->RPort,389,68);
	Draw(window->RPort,389,68-cputemp);
	SetAPen(window->RPort,0);
	Draw(window->RPort,389,19);
	SetDrPt(window->RPort,~0);
	SetAPen(window->RPort,1);
	Move(window->RPort,388,prevcpu);
	Draw(window->RPort,389,68-cputemp);
	prevcpu=68-cputemp;
}

void printfinetext(char *text,int x, int y, int color)
{
	int	tempy,
		tempx,
		len,
		x1,
		y1;
	struct RastPort *rastport=window->RPort;

	SetDrMd(rastport,JAM2);
	SetAPen(rastport,0);
	SetBPen(rastport,0);
	Move(rastport,x,y+2-FONTSIZE);
	len=strlen(text);
	tempy=y+2-FONTSIZE;
	tempx=x+len*8;
	x1=x+1;
	y1=y+1;

	WaitTOF();

	/* 	Uh, well .. I can't discover any better way to avoid the flickering.
		Don't place AmyInfo too high on your WB-screen :)
		It does not make any flickering on my big A, but I've got A2620..
		The following part will take ~5-> lines to be executed */

	Draw(rastport,tempx,tempy);
	SetAPen(rastport,1);
	Move(rastport,x1,y1);
	Text(rastport,text,len);
	SetDrMd(rastport,JAM1);
	SetAPen(rastport,color);
	Move(rastport,x,y);
	Text(rastport,text,len);
}

void idletask(void)	/*	This is Meisseli-Make! */
{
	ULONG suurinluku=1,taydellinenluku,luku;

	Disable();
		task->tc_Switch=switchroutine;
		task->tc_Flags|=TF_SWITCH;
	Enable();

	while(1)
	{
		taydellinenluku=0;
		for(luku=suurinluku;luku>1;luku--)
		{
			if(!(suurinluku%luku))
				taydellinenluku+=luku;
		}
		if(taydellinenluku==suurinluku)
			count++;
		suurinluku++;
	}
}

void __saveds switchroutine(void)
{
	/*	Thanks to Juhani Vehvilainen for sweatting with me because of
		this idle counting routine */

	cputime+=SysBase->Quantum-SysBase->Elapsed;
}

void main(int argc, char *argv[])
{
	ULONG	timersignal, 
			idcmpsignal, 
			signals, 
			class;
	UWORD	code, 
			qualifier;
	struct	IntuiMessage *msg=NULL;
	char	temp[80];
	int		i;

	if(argc==2 && (!strncmp(argv[1],"?",1) || !strncmp(argv[1],"-?",2)))
	{
		printf("\nUsage: AmyInfo <LEFT> <TOP>\n");
		printf("AmyInfo v1.3  Copyright (c) 1991 Juha Tuominen / Digital Design, Inc.\n\n");
	}
	else
	{
		if(argc>1)
		{
			for(i=1;i<=argc;i++)
			{
				if(!strncmp(argv[i],"-log",4))
					log=TRUE;
			}
		}
		if(argc>=3)
		{
			mywindow.LeftEdge=atoi(argv[1]);
			mywindow.TopEdge=atoi(argv[2]);
		}
		
		openthings();
		initgraphics();

		timersignal = 1L << timerport->mp_SigBit;
		idcmpsignal = 1L << window->UserPort->mp_SigBit;
		addtimerequest(0,1);

		task->tc_Node.ln_Type=NT_TASK;
		task->tc_Node.ln_Name=taskname;
		task->tc_Node.ln_Pri=-128;
		task->tc_SPLower=(APTR)stack;
		task->tc_SPUpper=(APTR)(STACK_SIZE+(ULONG)stack);
		task->tc_SPReg=task->tc_SPUpper;
		AddTask(task,idletask,0L);
		task_added=TRUE;

		while(keepgoing)
		{
			signals = Wait(timersignal | idcmpsignal);
			if(signals & timersignal)
			{
				GetMsg(timerport);
				addtimerequest(1,0);
				if(100*cputime/60<100)
					cputemp=cputime;
				cputemp2+=100*cputime/60;
				cpuload=cputemp2/++jakaja;
				cputime=0;
				currenttime();
				updatememorybar();
				sprintf(temp,"%d perfect numbers.",count);
				abouttext[6].IText=(UBYTE *)temp;
			}
			if(signals & idcmpsignal)
			{
				while(msg=(struct IntuiMessage *)GetMsg(window->UserPort))
				{
					class=msg->Class;
					code=msg->Code;
					qualifier=msg->Qualifier;
					ReplyMsg((struct Message *) msg);

					switch(class)
					{
						case CLOSEWINDOW:
							keepgoing=FALSE;
							break;
						case MOUSEBUTTONS:
							if(code==MENUDOWN)
								AutoRequest(NULL,&abouttext[0],NULL,&proceed,NULL,NULL,272,128);
					}
				}
			}
		}
	}
	cleanexit(0);
}
