/*
** AmigaNCP Monitor
** ----------------
**
** (C) 1993-1994 Oliver Wagner, All Rights Reserved
**
** Small'n'ugly hack to monitor NCP activity.
**
*/

#include <proto/dos.h>
#define __USE_SYSBASE
#include <proto/icon.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <workbench/startup.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include <libraries/ncplib.h>

#pragma libcall NCPBase NCP_private1 90 A01

/*
** Private data structures of amigancp.library
*/

struct NCP_Message
{
	struct Message m;
	short cmd;
	USHORT datasize;
	void *data;
	struct NCP_Channel *ncp_channel;
};

struct NCP_Channel
{
	ULONG readsigmask,			/* Signalled when new PKT arrived (Mask!) */
	 writesigmask;				/* Signalled when PKT send (Mask!) */

	ULONG flags;

	/*** the following is private!!! ***/

	USHORT thischannel,			/* Channel allocation */
	 thatchannel;

	char thisname[12], thatname[12];	/* "Process" names */

	struct List incoming;		/* incoming PKTs */
	struct List readbuffer;		/* incoming segments */

	struct MsgPort *readport, *writeport;
	struct NCP_Message readmsg, writemsg;

	ULONG bytesread, byteswritten;
};

#define NCPCF_ALLOC 1			/* Channel is allocated */
#define NCPCF_ACTIVE 2			/* Connections exists */
#define NCPCF_CONNREQ 4			/* Connection request underway */
#define NCPCF_REMOTECLOSED 8	/* Closed by remote */
#define NCPCF_OFFLINE	16		/* LLMAC is offline */
#define NCPCF_XOFF		32		/* Data transmission held */

struct NCP_Info
{
	ULONG rt, mt;
	UWORD remver;
	UWORD conn;
	ULONG skb, rkb;
};

#include "rev.h"
char version[]=
{"$VER: AmigaNCP-Monitor " VERTAG};

static struct NCP_Channel *ncp;
static struct Window *w;
static struct RastPort *rp;
static struct TextAttr top80 =
{"topaz.font", 8, 0, 0};
static struct MsgPort *infoport;
static struct timerequest *treq;
static struct NCP_Info ninfo;
static struct DiskObject *diskobj;

extern struct WBStartup *_WBenchMsg;

/*** Requires 2.04 ++ ***/
ULONG __oslibversion = 37;

/*** SAS/C cback.o startup */
char *__procname = "NCP Monitor";
ULONG __priority = -1;
ULONG __stack = 4000;
ULONG _BackGroundIO = FALSE;

#define text(x,y,s) Move(rp,x,y);Text(rp,s,strlen(s))

static void __stdargs 
sprintf(char *to, char *fmt,...)
{
	static UWORD fmtfunc[]=
	{ 0x16c0, 0x4e75};

	RawDoFmt(fmt, &fmt + 1, (APTR) fmtfunc, to);
}

void
cleanup(void)
{
	if (diskobj)
		FreeDiskObject(diskobj);
	if (treq)
	{
		if (treq->tr_node.io_Device)
			CloseDevice(treq);
		DeleteIORequest(treq);
	}
	if (infoport)
		DeletePort(infoport);
	if (w)
		CloseWindow(w);
}

static void
drawncp(int y, struct NCP_Channel *ncp)
{
	char buffer[80];

	sprintf(buffer, "%ld %-8.8s  %ld %-8.8s  %lc%lc%lc%lc%lc%lc  %10ld %10ld",
			ncp->thischannel, ncp->thisname,
			ncp->thatchannel, ncp->thatname,
			(ncp->flags & NCPCF_ALLOC) ? 'A' : '-',
			(ncp->flags & NCPCF_ACTIVE) ? 'A' : '-',
			(ncp->flags & NCPCF_CONNREQ) ? 'C' : '-',
			(ncp->flags & NCPCF_REMOTECLOSED) ? 'R' : '-',
			(ncp->flags & NCPCF_OFFLINE) ? 'O' : '-',
			(ncp->flags & NCPCF_XOFF) ? 'X' : '-',
			ncp->byteswritten, ncp->bytesread);

	text(4, y, buffer);
}

static char *
datstr(time_t t)
{
	struct tm *tm = localtime(&t);
	static char dat[64];

	if (!t)
		return ("--            ");

	sprintf(dat, "%02ld:%02ld:%02ld %02ld-%02ld",
			tm->tm_hour, tm->tm_min, tm->tm_sec,
			tm->tm_mday, tm->tm_mon + 1);

	return (dat);
}


static void
drawninfo(void)
{
	char buffer[80];

	NCP_private1(&ninfo);

	strcpy(buffer, datstr(ninfo.mt));
	text(116, 79, buffer);

	strcpy(buffer, datstr(ninfo.rt));
	text(116, 87, buffer);

	sprintf(buffer, "%ld  %9.9s", ninfo.remver, ninfo.conn ? "[Connect]" : NULL);
	text(332, 87, buffer);

	sprintf(buffer, "%10ld %10ld", ninfo.skb, ninfo.rkb);
	text(260, 79, buffer);

}

static void
drawinfo(void)
{
	SetDrMd(rp, JAM2);
	SetAPen(rp, 3);
	RectFill(rp, 0, 0, 432, 90);

	SetBPen(rp, 3);
	SetAPen(rp, 2);

	/* Infoline */
	text(4, 8, "# ThisProc  # RemotePr  Status  Bytes Sent  Received");
	text(4, 79, "Online since:                 =");
	text(4, 87, "Remote NCP..:                 Version:");
	Move(rp, 0, 10);
	Draw(rp, 431, 10);
	Move(rp, 0, 70);
	Draw(rp, 431, 70);

	SetAPen(rp, 1);
}

void __tzset( void )
{
	__timezone = 0;
}

int 
main(int argc, char **argv)
{
	struct IntuiMessage *im, imsg;
	int c;
	ULONG sigs, infosig, idcmpsig;
	char windowtitle[ 128 ];
	USHORT windowx = 0, windowy = 0;
	char *iconname;
	struct TextFont *tf;

	/* Are we already running? */
	Forbid();
	infoport = FindPort("NCP Monitor");
	if (infoport)
	{
		Signal(infoport->mp_SigTask, SIGBREAKF_CTRL_F);
		Permit();
		return (0);
	}
	infoport = CreatePort("NCP Monitor", -128);
	infosig = 1 << infoport->mp_SigBit;
	Permit();

	onexit(cleanup);

	/*** Load Icon ***/
	if (argc)
		diskobj = GetDiskObjectNew(iconname = argv[0]  );
	else
		diskobj = GetDiskObjectNew(iconname = _WBenchMsg->sm_ArgList[0].wa_Name);

	if (diskobj)
	{
		char *def = FindToolType(diskobj->do_ToolTypes, "WINDOW");

		if (def)
		{
			windowx = atoi(def);
			def = strchr(def, '/');
			if (def++)
			{
				windowy = atoi(def);
			}
		}
	}

	sprintf(windowtitle, "AmigaNCP Monitor - amigancp.library v%ld.%ld [%s]",
			NCPBase->lib_Version,
			NCPBase->lib_Revision,
			NCPBase->lib_IdString
		);

	/*** Open Window ***/
	w = OpenWindowTags(NULL,
					   (windowx) ? WA_Left : TAG_IGNORE, windowx,
					   (windowy) ? WA_Top : TAG_IGNORE, windowy,
					   WA_InnerWidth, 432,
					   WA_InnerHeight, 90,
					   WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW,
					   WA_Flags, WINDOWDRAG | WINDOWDEPTH | RMBTRAP | GIMMEZEROZERO | WINDOWCLOSE | SIMPLE_REFRESH | WINDOWSIZING,
					   WA_MinWidth, 120,
					   WA_MinHeight, 25,
					   WA_Title, windowtitle,
					   WA_ScreenTitle, &version[ 6 ],
					   WA_AutoAdjust, TRUE,
					   TAG_DONE
		);

	if (!w)
	{
		PutStr("can't open window");
		return (20);
	}

	treq = CreateIORequest( infoport, sizeof( *treq ) );
	OpenDevice( "timer.device", UNIT_VBLANK, treq, 0 );

	idcmpsig = 1 << w->UserPort->mp_SigBit;

	rp = w->RPort;
	tf = OpenFont( &top80 );
	SetFont( rp, tf );

	drawinfo();

	ncp = NCP_private1(NULL);

	/*** Main Loop ***/
	FOREVER
	{
		SetSignal(0, infosig);

		for (c = 0; c < 7; c++)
			drawncp(19 + c * 8, &ncp[c]);

		drawninfo();

		treq->tr_node.io_Command = TR_ADDREQUEST;
		treq->tr_time.tv_secs = ( w->Flags & WFLG_WINDOWACTIVE ) ? 1 : 3;
		treq->tr_time.tv_micro = 0;

		SendIO(treq);

		sigs = Wait(infosig | idcmpsig | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F);

		/* Abort Timer */
		AbortIO(treq);
		WaitIO(treq);

		im = (struct IntuiMessage *) GetMsg(w->UserPort);
		if (im)
		{
			imsg = *im;
			ReplyMsg(im);
			if (imsg.Class == CLOSEWINDOW)
				break;
			else if (imsg.Class == REFRESHWINDOW)
			{
				BeginRefresh(w);
				drawinfo();
				EndRefresh(w, TRUE);
			}
		}

		if (sigs & SIGBREAKF_CTRL_C)
			break;

		if (sigs & SIGBREAKF_CTRL_F)
			WindowToFront(w);
	}

	/*** Save Icon ***/
	if (diskobj)
	{
		char *tt[2];

		sprintf(windowtitle, "WINDOW=%ld/%ld",
				w->LeftEdge,
				w->TopEdge
			);

		tt[0] = windowtitle;
		tt[1] = NULL;

		diskobj->do_ToolTypes = tt;
		PutDiskObject(iconname, diskobj);
	}

	if( tf )
		CloseFont( tf );

	/*** Exit ***/
	return (0);
}
