/*
	VolumeWatch
	-----------

	whooa! again a 2 hours production ;-)

	- should have been used BOOPSI objects for the frames

	Copyright ©1996 by Daniel Balster. Freeware!
*/

#include <exec/exec.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <proto/dos.h>
#include <intuition/intuition.h>
#include <devices/timer.h>
#include <proto/intuition.h>
#include <proto/graphics.h>

#include <stdio.h>
#include <string.h>

#define SCANFLAGS LDF_VOLUMES|LDF_READ
#define TEMPLATE "PUBSCREEN/K,WIDTH/K/N,LEFT/K/N,TOP/K/N,INTERVAL/K/N,NOFULL/S,QUIET/S"

#define LOCALETEMP "%ld%% full, %ld%c free, %ld%c used"
//#define LOCALETEMP "%ld%% voll, %ld%c frei, %ld%c benutzt"

UBYTE *version = "$VER: VolumeWatch 1.1 (29.01.96) Copyright ©1996 by Daniel Balster";

struct {
	STRPTR pubscreen;
	ULONG  *Width, *Left, *Top, *interval;
	ULONG  nofull;
	ULONG quiet;
} args = {0};

struct VolumeInfo
{
	struct Node node;		// to link
	WORD pad;				// force the next one to a 32bit boundary
	struct InfoData data;	// Info'ed
	UBYTE *name;			// AllocVec'ed
};

SHORT zoom[4] = { 0,0,100,14 };

char *units = "KMG";	// Kilo-Mega-Giga

// *** timer

struct MsgPort		*time_mp = 0;
struct timerequest	*time_ior = 0;

#define TIMESIGNAL (1l<<time_mp->mp_SigBit)

int inittimer()
{
	if (time_mp = CreateMsgPort())
	{
		if (time_ior = (struct timerequest*) CreateIORequest (time_mp,sizeof(struct timerequest)))
		{
			if (OpenDevice ("timer.device",UNIT_MICROHZ,time_ior,0)==0)
			{
				time_ior->tr_node.io_Command	= TR_ADDREQUEST;
				time_ior->tr_node.io_Flags	= IOF_QUICK;
				time_ior->tr_time.tv_secs	= 1;
				time_ior->tr_time.tv_micro	= 0;
				SendIO (time_ior);

				return 1;
			}
		}
	}
	
	return 0;
}

VOID freetimer()
{
	if (time_mp)
	{
		if (time_ior)
		{
			AbortIO (time_ior);
			WaitIO (time_ior);
			CloseDevice (time_ior);
			DeleteIORequest ((struct IORequest*) time_ior);
		}
		DeleteMsgPort (time_mp);
	}
}

VOID WindowMagic (struct Window *win, struct List *list, UWORD items)
{
	if (win)
	{
		struct RastPort *RP = win->RPort;
		ULONG height,width;
		struct VolumeInfo *vinfo;
		struct TextExtent te;
		int i,j,x,y;
		ULONG free,inuse,percent,total;
		char buf[80], unit1, unit2;
		LONG sigs;
		BOOL waiting = TRUE;
		BYTE dark,light,fill,hilight,backfill;
		struct DrawInfo *dri;

		if (win->Flags & WFLG_ZOOMED) return;

		if (dri = GetScreenDrawInfo(win->WScreen))
		{
			SetFont(RP,dri->dri_Font);

			dark     = dri->dri_Pens[SHADOWPEN];
			light    = dri->dri_Pens[SHINEPEN];
			fill     = dri->dri_Pens[FILLPEN];
			hilight  = dri->dri_Pens[TEXTPEN];
			backfill = dri->dri_Pens[BACKGROUNDPEN];

			FreeScreenDrawInfo(win->WScreen,dri);
		}
		else	// old method...
		{
			dark     = 1;
			light    = 2;
			fill     = 3;
			hilight  = 1;
			backfill = 0;
		
			SetFont(RP,win->WScreen->RastPort.Font);
		}
		
		height = (items*RP->TxHeight*3) + win->BorderTop + win->BorderBottom;
		width = (win->Width-(win->BorderLeft+win->BorderRight))/2;

		if (win->Height != height)
		{
			ChangeWindowBox(win,win->LeftEdge,win->TopEdge,win->Width,height);

			while (waiting)
			{
				sigs = Wait(SIGBREAKF_CTRL_D|(1L << win->UserPort->mp_SigBit));

				if (sigs & SIGBREAKF_CTRL_D) waiting = FALSE;

				if (sigs & (1L << win->UserPort->mp_SigBit))
				{
					struct IntuiMessage *imsg;

					while (imsg = (struct IntuiMessage*) GetMsg(win->UserPort))
					{
						switch (imsg->Class)
						{
							case IDCMP_CHANGEWINDOW:
								waiting = FALSE;
								break;
							default:
								break;
						} // switch
						ReplyMsg((struct Message*)imsg);
					} // while
				} // if WindowSignal
			} // while
		} // if 

		// a list parser
		i=0;
		for (vinfo = (struct VolumeInfo*)list->lh_Head;vinfo->node.ln_Succ;vinfo=(struct VolumeInfo*)vinfo->node.ln_Succ)
		{
			x = win->BorderLeft+4;
			y = win->BorderTop+i+(RP->TxHeight/2);

			SetABPenDrMd(RP,hilight,backfill,JAM2);
			SetSoftStyle(RP,2,7);
			// width is the half window width (minus borders) and each 4 pixels left and right
			Move (RP,x,y+RP->TxBaseline);
			Text (RP,vinfo->name,TextFit(RP,vinfo->name,strlen(vinfo->name)-1,&te,NULL,1,width-8,32737));
			SetAPen(RP,backfill);
			RectFill(RP,RP->cp_x,y,x+width-4,y+RP->TxHeight-1);

			x += width;		// drift to the right

			total = vinfo->data.id_NumBlocks*vinfo->data.id_BytesPerBlock;
			inuse = vinfo->data.id_NumBlocksUsed*vinfo->data.id_BytesPerBlock;
			free  = total-inuse;
			percent = (vinfo->data.id_NumBlocksUsed*100)/vinfo->data.id_NumBlocks;

			// well, this is ugly; should have used locale.lib ;-(
			
			j=0; unit2 = 'K'; inuse &=~ 0x3ff;
			while (inuse >= 1024)
			{
				inuse /= 1024;
				unit2 = units[j++];
			} // while
			j=0; unit1 = 'K'; free &=~ 0x3ff;
			while (free >= 1024)
			{
				free /= 1024;
				unit1 = units[j++];
			} // while
			
			sprintf(buf,LOCALETEMP,percent,free,unit1,inuse,unit2);

			SetABPenDrMd(RP,hilight,backfill,JAM2);
			SetSoftStyle(RP,0,7);
			// width is the half window width (minus borders) and each 4 pixels left and right
			Move (RP,win->BorderLeft+4,y+RP->TxBaseline+4+RP->TxHeight);
			Text (RP,buf,TextFit(RP,buf,strlen(buf),&te,NULL,1,win->Width-(8+win->BorderLeft+win->BorderRight),32737));

			SetAPen(RP,backfill);
			RectFill(RP,RP->cp_x,y+4+RP->TxHeight,win->Width-(win->BorderLeft+win->BorderRight),y+4+(2*RP->TxHeight)-1);
			
			SetAPen(RP,dark);
			Move (RP,x+width-8-1,y);
			Draw (RP,x,y);
			Draw (RP,x,y+RP->TxHeight);
			SetAPen(RP,light);
			Move (RP,x+1,y+RP->TxHeight);
			Draw (RP,x+width-8,y+RP->TxHeight);
			Draw (RP,x+width-8,y);
			SetAPen(RP,fill);
			RectFill(RP,x+1,y+1,x+(((width-10)*percent)/100)+1,y+RP->TxHeight-1);
			SetAPen(RP,backfill);
			if ((((width-10)*percent)/100)!=width-10) RectFill(RP,x+(((width-10)*percent)/100)+1,y+1,x+(width-10),y+RP->TxHeight-1);
			
			i += RP->TxHeight*3;
		} // for
	} // if
}

int main ()
{
	struct RDArgs *rda;
	VOID *mypool;
	struct Process *proc = (struct Process*) FindTask(NULL);
	APTR old;
	
	old = proc->pr_WindowPtr;
	proc->pr_WindowPtr = -1;

	if (rda = ReadArgs(TEMPLATE,(LONG*)&args,NULL))
	{
		struct Screen *scr;
		struct Window *win;

		if (!args.quiet) PutStr(
"*** VolumeWatch 1.1 ***\n"
"Copyright © 1996 by Daniel Balster\n"
"All Rights Reserved.\n");


		if (args.interval) if (!(inittimer())) goto toobad;

		// if no pubscreen is given as argument, default is used
		if (scr = LockPubScreen(args.pubscreen))
		{
			zoom[0] = args.Left ? *args.Left : ~0,
			zoom[1] = args.Top ? *args.Top : ~0,
			zoom[2] = args.Width ? *args.Width : scr->Width/5;
			zoom[3] = scr->BarHeight+1;

			win = OpenWindowTags(NULL,
				WA_PubScreen		, scr,
				WA_Width			, args.Width ? *args.Width : scr->Width/5,
				WA_Left			, args.Left ? *args.Left : ~0,
				WA_Top			, args.Top ? *args.Top : ~0,
				WA_Height			, scr->BarHeight+1,
				WA_CloseGadget		, TRUE,
				WA_DepthGadget		, TRUE,
				WA_DragBar		, TRUE,
				WA_RMBTrap		, TRUE,
				WA_Zoom			, zoom,
				WA_Title			, "VolumeWatch",
				WA_IDCMP			, IDCMP_CLOSEWINDOW |
								  IDCMP_DISKINSERTED |
								  IDCMP_MOUSEBUTTONS |
								  IDCMP_CHANGEWINDOW |
								  IDCMP_DISKREMOVED,
				TAG_DONE);

			// we don't need to hold the screen any longer
			UnlockPubScreen(NULL,scr);
			
			if (win)
			{
				if (mypool = CreatePool(MEMF_PUBLIC|MEMF_CLEAR,4096,4096))
				{
					struct List mylist;
					struct DosList *dlist;
					struct VolumeInfo *vinfo;
					BPTR mylock;
					BOOL running = TRUE;
					UWORD entries;
					LONG signals;

					while (running)
					{
						NewList(&mylist);
						entries = 0;

						dlist = LockDosList(SCANFLAGS);
						while (dlist = NextDosEntry(dlist,SCANFLAGS))
						{
							UBYTE *name = BADDR(dlist->dol_Name);
							ULONG length = (ULONG)name[0];
							BOOL fail = TRUE;
						
							if (vinfo = AllocPooled(mypool,sizeof(*vinfo)))
							{
								if (vinfo->name = AllocPooled(mypool,length+2))
								{
									CopyMem(++name,vinfo->name,length);
									vinfo->name[length] = ':';
									
									if (mylock = Lock(vinfo->name,ACCESS_READ))
									{
										if (Info(mylock,&vinfo->data))
										{
											if (args.nofull)
											{
												if (vinfo->data.id_NumBlocks != vinfo->data.id_NumBlocksUsed)
												{
													AddHead(&mylist,(struct Node*)vinfo);
													entries++;
													fail = FALSE;
												} // if
											} // if
											else
											{
												AddHead(&mylist,(struct Node*)vinfo);
												entries++;
												fail = FALSE;
											} // if..else
										} // if Info
										UnLock(mylock);
									} // if Lock
									if (fail) FreePooled(mypool,vinfo->name,strlen(vinfo->name)+1);
								} // if AllocPooled
								if (fail) FreePooled(mypool,vinfo,sizeof(*vinfo));
							} // if AllocPooled
						} // while NextDosEntry

						WindowMagic(win,&mylist,entries);

						// cleanup 
						while (!IsListEmpty(&mylist))
						{
							if (vinfo = (struct VolumeInfo*) RemHead(&mylist))
							{
								// note: we have added a nonzero character, so the length is now +1, not +2
								if (vinfo->name) FreePooled(mypool,vinfo->name,strlen(vinfo->name)+1);
								FreePooled(mypool,vinfo,sizeof(*vinfo));
							} // if RemHead
						} // while IsListEmpty

						UnLockDosList(SCANFLAGS);
						
						signals = Wait(TIMESIGNAL|SIGBREAKF_CTRL_C|(1L << win->UserPort->mp_SigBit));

						if (signals & SIGBREAKF_CTRL_C) running = FALSE;

						if (!(win->Flags & WFLG_ZOOMED)) if (time_mp) if (signals & TIMESIGNAL)
						{
							time_ior->tr_node.io_Command	= TR_ADDREQUEST;
							time_ior->tr_node.io_Flags	= IOF_QUICK;
							time_ior->tr_time.tv_secs	= *args.interval;
							time_ior->tr_time.tv_micro	= 0;
							SendIO (time_ior);
						}

						if (signals & (1L << win->UserPort->mp_SigBit))
						{
							struct IntuiMessage *imsg;
							
							while (imsg = (struct IntuiMessage*) GetMsg(win->UserPort))
							{
								switch (imsg->Class)
								{
									case IDCMP_CLOSEWINDOW:
										running = FALSE;
										break;
							// no need 
							//		case IDCMP_DISKINSERTED:
							//		case IDCMP_DISKREMOVED:
							//			break;
									default:
										break;
								}
								ReplyMsg((struct Message*)imsg);
							}
						} // if WindowSignal

					} // while
					DeletePool(mypool);
				} // if CreatePool
				if (win) CloseWindow (win);
			} // if OpenWindow
		} // if LockPubScreen

	toobad:
		freetimer();	// bad style ?

		FreeArgs(rda);
	} // if ReadArgs
	
	proc->pr_WindowPtr = old;

	return 0;
}
