/*
 *  Modifications to existing pc.c module are --
 *
 *  Copyright (c) 1987
 *  Louis A. Mamakos
 *
 *  This work, or any derivations thereof may be used for non-commercial
 *  purposes only.  So there.
 */

/* OS- and machine-dependent stuff for the Commodore-Amiga 1000 */

#define	AMIGAVERSION	"3"

#include <exec/types.h>
#include <functions.h>		
/* for Manx Aztec C, get func returns */
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/io.h>

#include <devices/console.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <intuition/intuition.h>

#include <stdio.h>
#include "machdep.h"
#include "amiga.h"
#include "mbuf.h"
#include "internet.h"
#include "iface.h"
#include "cmdparse.h"
#include "slip.h"
#include "timer.h"
#include "netuser.h"
#include "ip.h"
#include "tcp.h"
#ifdef	TRACE
#include "trace.h"
#endif

static char *copyright_notice[2] = {
	"AMIGA port of KA9Q TCP/IP (C) Copyright 1987 Louis A. Mamakos\r\n",
	"for non-commercial, non-profic use only\r\n"};

struct asy asy[ASY_MAX];

void *malloc();

void setiss();

/* Interface list header */
struct interface *ifaces;

struct IntuitionBase *IntuitionBase;
static char banner[80];
static struct NewWindow nw = {
	0, 0, 640, 200,		/* left, top, (max) width, (max) height */
	0, 1,			/* detail pen, block pen */
	0,			/* IDCMP flags */
	SMART_REFRESH | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING |
	    SIZEBBOTTOM | ACTIVATE | NOCAREREFRESH,	/* window flags */
	NULL, NULL,		/* gadget, checkmark */
	(UBYTE *)&banner[0],	/* title of window */
	NULL, NULL,		/* screen, bitmap */
	200, 50, -1, -1,	/* sizing limits */
	WBENCHSCREEN,		/* on the workbench */
};

APTR	oldwindowptr;
struct Process *mytask;

struct MsgPort *keyboard, *consinp, *consoutp, *serinp, *seroutp,
		      *timerp;
struct IOExtSer serin, serout;
struct IOStdReq consin, consout;
struct timerequest tr;
struct Window *win;

char InputCharacter;

int timeropen, serialopen;
#ifdef AMIGADEVDRV
int DeviceSignal;
#endif
struct timer worktimer;		/* this is NOT a timer.device timer */
void worker();
#ifdef LATTICE
extern struct { short error; char *msg; } os_errlist[];
extern int _OSERR, os_nerr;
#endif
static
clean(why)
	char *why;
{
	int i;

#ifdef AMIGADEVDRV
	if (DeviceSignal >= 0)
		FreeSignal(DeviceSignal);
#endif
	if (timeropen)
		CloseDevice(&tr);
	if (serialopen)
		CloseDevice(&serin);
	if (win)
		CloseWindow(win);
	if (consinp)
		DeletePort(consinp);
	if (consoutp)
		DeletePort(consoutp);
	if (serinp)
		DeletePort(serinp);
	if (seroutp)
		DeletePort(seroutp);
	if (timerp)
		DeletePort(timerp);

	mytask->pr_WindowPtr = oldwindowptr;

	if (why) {
           myoserr(why);
	}
	exit(0);
}
myoserr(why)
char *why;
{
  int i;
		fprintf(stderr, "%s: ", why); 
#ifdef LATTICE
		fprintf(stderr, "%d: ", _OSERR);

		for(i = 0; os_errlist[i].error < os_nerr; i++)
		  if (os_errlist[i].error == _OSERR)
			fprintf(stderr, os_errlist[i].msg);
#endif
		fprintf(stderr, "\r\n");
}
/* Called at startup time to set up console I/O, memory heap */
ioinit()
{
	extern char major_rev[], minor_rev[];
	struct Screen *scr;

	mytask = (struct Process *) FindTask((char *) NULL);
	oldwindowptr = mytask->pr_WindowPtr;
	mytask->pr_WindowPtr = (APTR) -1;	/* disable DOS requestors */

	if ((IntuitionBase = (struct IntuitionBase *)
	   OpenLibrary("intuition.library", 33L)) == NULL)
		clean("No intuition: Version 1.2 of Amiga Systems Software required");

	sprintf(banner,
#ifdef	AMIGADEVDRV
	"KA9Q Internet Protocol Package, v%s.%s (Amiga version %sD)",
#else
	"KA9Q Internet Protocol Package, v%s.%s (Amiga version %s)",
#endif
		major_rev, minor_rev, AMIGAVERSION);

	/*
	 *  Try to determine the size of the workbench screen
	 */
	scr = malloc(sizeof(struct Screen));
	if (scr==NULL)
		clean("Can't alloc screen");

	if (GetScreenData(scr, (ULONG) sizeof(struct Screen),
			  WBENCHSCREEN, NULL) == TRUE) {
		nw.Width = scr->Width;
		nw.Height = scr->Height-20;
		nw.TopEdge = 19;
	} else
		fprintf(stderr, "Can't GetScreenData()\n");

	free((char *)scr);
	if ((win = OpenWindow(&nw)) == NULL)
		clean("Can't open window");

	if ((consinp = CreatePort("net:console in", 0L)) == NULL)
		clean("Can't create console port");

	if ((consoutp = CreatePort("net:console out", 0L)) == NULL)
		clean("Can't create console port");

	if ((timerp = CreatePort("net:timer", 0L)) == NULL)
		clean("Can't create timer port");

	consin.io_Data = (APTR) win;
	consin.io_Length = sizeof(struct Window);

	if (OpenDevice("console.device", 0L, &consin, 0L) != 0L)
		clean("Can't open console device");

	consout = consin;

	consin.io_Message.mn_ReplyPort = consinp;
	consin.io_Length = 1;
	consin.io_Data = (APTR) &InputCharacter;
	consin.io_Command = CMD_READ;
	SendIO(&consin);
	consout.io_Message.mn_ReplyPort = consoutp;
	consout.io_Command = CMD_WRITE;

	/* create and start up timer */
	tr.tr_node.io_Message.mn_ReplyPort = timerp;
	if (OpenDevice("timer.device", UNIT_VBLANK, &tr, 0L) != 0L)
		clean("Can't open timer");
#ifdef AMIGADEVDRV
	if ((DeviceSignal = AllocSignal(-1)) == -1)
	  clean("Can't allocate device signal");
#endif
	timeropen++;
	tr.tr_node.io_Command = TR_GETSYSTIME;
	DoIO(&tr);
#ifdef	DEBUG
	printf("System time is %ld\n", tr.tr_time.tv_secs);
#endif
	setiss(tr.tr_time.tv_secs);
	tr.tr_node.io_Command = TR_ADDREQUEST;
	tr.tr_time.tv_secs = 0;
	tr.tr_time.tv_micro = MSPTICK*1000L;	/* convert to microseconds */
	SendIO(&tr);

	set_timer(&worktimer, 1500);	/* set for 1.5 seconds */
	worktimer.func = worker;
#ifdef	AMIGADEVDRV
	DriverInit();			/* install internet.device driver */
#endif
	start_timer(&worktimer);
}

/* Called just before exiting to restore console state */
iostop()
{
	while(ifaces != NULLIF){
		if(ifaces->stop != NULLFP)
			(*ifaces->stop)(ifaces);
		ifaces = ifaces->next;
	}
#ifdef	AMIGADEVDRV
	DriverShutdown();
#endif
	clean((char *)0);
}

#define	BUFMAXCNT	150
static char conbuf[BUFMAXCNT];
static int concnt = 0;

int
amigaputchar(c)
	char c;
{
	conbuf[concnt++] = c;
	if ((c == '\n') || (concnt == BUFMAXCNT))
		amigaflush();
	return c;
}

amigaflush()
{
	if (concnt == 0)
		return;
	consout.io_Data = (APTR) conbuf;
	consout.io_Length = concnt;
	consout.io_Command = CMD_WRITE;
	DoIO(&consout);
	concnt = 0;
}
	
/*
 *  Begin terrible, horrible hack.  All output should be printed upon (into?)
 *  the window we opened before.  Here goes nothing...
 */
void
printf(a, b, c, d, e, f, g, h, i, j, k)
	char *a;
	int b, c, d, e, f, g, h, i, j, k;
{
	if (concnt)
		amigaflush();

	sprintf(conbuf, a, b, c, d, e, f, g, h, i, j, k);
	consout.io_Data = (APTR) conbuf;
	consout.io_Length = strlen(conbuf);
	consout.io_Command = CMD_WRITE;
	DoIO(&consout);		/* no use in doing this async */
}

	
/* check active connections and update titles */
void
check_connections()
{
	extern struct tcb *tcbs[NTCB];
	register struct tcb *tcb;
	register int i;
	int newlisten, newopn;
	static int listen = -1, opn = -1;
	static msg[80];

	newlisten = newopn = 0;

	for(i=0; i<NTCB; i++)
		for(tcb=tcbs[i]; tcb != NULLTCB; tcb = tcb->next)
			if (tcb->state == LISTEN)
				newlisten++;
			else
				newopn++;

	if (newlisten != listen || newopn != opn) {
		listen = newlisten;
		opn = newopn;
		sprintf(msg,
	   "Amiga Port by WA3YMH (TCP: listen: %d   open: %d)", listen, opn);
		SetWindowTitles(win, -1L, msg);
	}
}

/* called every second or so */
void
worker()
{
	check_connections();
	start_timer(&worktimer);
}

#if	0
/* processes any messages that Intuition sends us */
void
Do_Intuition_Message(m)
	register struct IntuiMessage *m;
{
	ULONG class;
	USHORT code, qualifier;

	class = m->Class;
	code = m->Code;
	qualifier = m->Qualifier;
	ReplyMsg((struct Message *) m);	   /* reply msg back to Intuition */
	switch (class) {
	case INTUITICKS:
		check_connections();		
		break;
	}
}
#endif

	
/*
 * wait for something to happen
 */
eihalt()
{
	register struct IntuiMessage *msg;
	static ULONG mask = 0;

	if (mask == 0L)
		mask = 	1L << consinp->mp_SigBit |
			1L << serinp->mp_SigBit |
			1L << timerp->mp_SigBit |
#ifdef AMIGDEVDRV
			1L << DeviceSignal	|
#endif
#if	0
			1L << win->UserPort->mp_SigBit |
#endif
			1L << seroutp->mp_SigBit;

	(void) Wait(mask);
#if	0
	while (msg = (struct IntuiMessage *)GetMsg(win->UserPort))
		Do_Intuition_Message(msg);
#endif
}

/* checks the time then ticks and updates ISS */
void
check_time()
{
	int32 iss();

	if (CheckIO(&tr)) {
		WaitIO(&tr);
		(void) GetMsg(timerp);
		tick();
		(void)iss();
		tr.tr_time.tv_secs = 0;
		tr.tr_time.tv_micro = MSPTICK*1000L;
					/* convert to microseconds */
		SendIO(&tr);
	}
}

/* Initialize asynch port "dev" */


/*
 *  We will make the bold and rash assumption that the asy link will be used
 *  for slip and slip-like stuff.  That is, we assume that there is an
 *  an end-of-frame character that we can have the serial.device driver look
 *  for.  Thus, we can fire up a single I/O request and have the whole frame
 *  come back at once.
 */
int
asy_init(dev, bufsize)
int16 dev;
unsigned bufsize;
{
	char serinitstr[1];
	int serinitlen;

	serinitstr[0] = FR_END;	/* initialize initialization string to a */
	serinitlen = 1;		/* frame end to flush receiver */

	if (serialopen) {
		printf("\namiga: Error - serial device already open.\n");
		return 0;
	}
	if ((serinp = CreatePort("net:serin", 0L)) == NULL)
		clean("Can't create serial input port");

	if ((seroutp = CreatePort("net:serout", 0L)) == NULL)
		clean("Can't create serial output port");

	/*
	 * Open serial device.
	 */
	serin.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;  /* ? */
	serin.io_Status = 0;
	serin.io_RBufLen = bufsize;
	asy[dev].speed = serin.io_Baud = 2400;	/* default speed */
	if (OpenDevice("serial.device", 0L, &serin, 0L) != 0L)
		clean("Can't open serial device");
	serialopen++;
	serin.IOSer.io_Message.mn_ReplyPort = serinp;
	serout = serin;
	serout.IOSer.io_Message.mn_ReplyPort = seroutp;
	asy[dev].buflen = bufsize;
	/* alloc input buffer */
	if((asy[dev].input_buffer = malloc(asy[dev].buflen)) == NULL)
		clean("Can't allocate serial input buf");
	serin.IOSer.io_Data = (APTR) asy[dev].input_buffer;
	serin.IOSer.io_Length = asy[dev].buflen;
	asy[dev].input_len = 0;		/* clear input buffer */
	serin.io_SerFlags = SERF_XDISABLED|SERF_EOFMODE|SERF_RAD_BOOGIE;
	serin.IOSer.io_Flags = 0;
	SendIO(&serin);
	serout.IOSer.io_Data = (APTR) serinitstr;
	serout.IOSer.io_Length = serinitlen;
	serout.IOSer.io_Command = CMD_WRITE;
	serout.IOSer.io_Flags = 0;
	serout.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
	DoIO(&serout);
}

int
asy_stop(iface)
struct interface *iface;
{
	if (iface->dev >= ASY_MAX) {
		fprintf(stderr, "asy_stop: bad dev %d\n", iface->dev);
		return;
	}
	AbortIO(&serin);
	AbortIO(&serout);
	CloseDevice(&serin);
	free(asy[iface->dev].input_buffer);	/* release buffer */
	serialopen--;
}

/* Set asynch line speed */
int
asy_speed(dev,speed)
int dev;
int speed;
{
	if (serialopen == 0)
		return;

	AbortIO(&serin);
	WaitIO(&serin);
	(void) GetMsg(serinp);
	asy[dev].speed = serin.io_Baud = speed;
	serin.io_ReadLen = 8;
	serin.io_WriteLen = 8;
	serin.io_StopBits = 1;
	serin.io_TermArray.TermArray0 =	serin.io_TermArray.TermArray1 = 
	   (ULONG)FR_END << 24 | (ULONG)FR_END << 16 | FR_END << 8 | FR_END;
	serin.IOSer.io_Command = SDCMD_SETPARAMS;
	serin.IOSer.io_Flags = 0;
	DoIO(&serin);
	if (serin.IOSer.io_Error)
		printf("Bad I/O status %d on SETPARAMS\n",
					serin.IOSer.io_Error);

	serin.IOSer.io_Command = CMD_READ;
	serin.IOSer.io_Data = (APTR) asy[dev].input_buffer;
	serin.IOSer.io_Length = asy[dev].buflen;
	asy[dev].input_len = 0;		/* clear input buffer */
	serin.io_SerFlags = SERF_XDISABLED|SERF_EOFMODE|SERF_RAD_BOOGIE;
	serin.IOSer.io_Flags = 0;	/* no quick I/O */
	SendIO(&serin);
}

/* Send a buffer to serial transmitter */
asy_output(dev,buf,cnt)
unsigned dev;
char *buf;
unsigned short cnt;
{
	/*
	 *  We 'know' that the transmitter is ready since we would not have
	 *  been called unless the previous I/O has been completed.
	 */
	WaitIO(&serout);
	GetMsg(seroutp);
	serout.IOSer.io_Data = (APTR) buf;
	serout.IOSer.io_Length = cnt;
	serout.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
	serout.IOSer.io_Flags = 0;	/* no quick I/O */
	serout.IOSer.io_Command = CMD_WRITE;
	SendIO(&serout);
}

/* Read characters from the keyboard, translating them to "real" ASCII
 * If none are ready, return the -1 from kbraw()
 */
kbread()
{
	char c;

	if (CheckIO(&consin)) {
		WaitIO(&consin);
		(void) GetMsg(consinp);
		c = InputCharacter;
		consin.io_Length = 1;
		consin.io_Data = (APTR) &InputCharacter;
		consin.io_Command = CMD_READ;
		SendIO(&consin);		/* start next read up */
		return (c & 0xff);
	}

	return -1;		/* nuthin here */
}

/* Receive characters from asynch line
 * Returns count of characters read
 */
unsigned
asy_recv(dev,buf,cnt)
int dev;
char *buf;
unsigned cnt;
{
	register int actual = 0;
	register long error;

	if (asy[dev].input_len == 0) {		/* if buffer is empty.. */
		if (CheckIO(&serin) == NULL)	/* see if I/O has completed */
			return 0;		/* nope, not yet. */
		if (error = WaitIO(&serin))
			printf("(SERIN) WaitIO returns %d\n", error);
		if (serin.IOSer.io_Error)
			printf("Bad I/O stat %d (SERIN)\n",
					serin.IOSer.io_Error);
		(void) GetMsg(serinp);
		/* input has completed.  fill in state variables */
		asy[dev].input_len = serin.IOSer.io_Actual;
		asy[dev].input_p = asy[dev].input_buffer;
#ifdef	TRACE
		if (trace & 0x40000000) {
			int a, n, l = asy[dev].input_len;
			unsigned char *b = asy[dev].input_buffer;

			a = 0;
			printf("Raw serial input:\r\n");
			while (l) {
				n = min(l, 16);
				fmtline(a, b, n);
				a += n;
				b += n;
				l -= n;
			}
			fflush(stdout);
		}
#endif			
	}

	if (asy[dev].input_len) {	/* any chars in buffer left?  */
		actual = min(asy[dev].input_len, cnt);
		if (actual == 1)
			*buf = *asy[dev].input_p;	/* usual case */
		else
			movmem(asy[dev].input_p, buf, actual);
		asy[dev].input_len -= actual;
		asy[dev].input_p += actual;
	}

	if (asy[dev].input_len == 0) {		/* if buffer is now empty */
		serin.IOSer.io_Command = CMD_READ;
		serin.IOSer.io_Data = (APTR) asy[dev].input_buffer;
		serin.IOSer.io_Length = asy[dev].buflen;
		serin.io_SerFlags = 
			SERF_XDISABLED|SERF_EOFMODE|SERF_RAD_BOOGIE;
		serin.IOSer.io_Flags = 0;	/* no quick I/O */
		SendIO(&serin);
	}

	return actual;
}

int
stxrdy(dev)
{
	return (CheckIO(&serout) != NULL);
}

/* Create a directory listing in a temp file and return the resulting file
 * descriptor. If full == 1, give a full listing; else return just a list
 * of names.
 *
 * This function is very dependent on the workings of Aztec standard I/O;
 * it uses their mechanism for generating and deleting temporary files.
 */
FILE *
dir(path,full)
char *path;
int full;
{
	/*return (FILE *)NULL;*/
	return(0L);
}

#if	0
bcmp(a,b,n)
register char *a,*b;
register int16 n;
{
	while(n-- != 0){
		if(*a++ != *b++)
			return 1;
	}
	return 0;
}
#endif
