#include <stdio.h>		/* XXX */

#include <exec/types.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <devices/console.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#ifndef AZTEC_C
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/console.h>
#endif

#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "socket.h"
#include "proc.h"
#include "session.h"
#include "tty.h"
#include "amiga/windows.h"
#include "hardware.h"
#include "commands.h"

extern struct proc *Display;
void display __ARGS((int i, void *v1, void *v2));

static void CloseWindowSafely __ARGS((struct Window *w));
static void conputc __ARGS((struct WND_session *wnd, char c));

struct MsgPort *WND_msgport;
struct IntuitionBase *IntuitionBase;
struct Library *ConsoleDevice;

static struct MsgPort *DOIO_msgport;
static struct IOStdReq cd_ioreq;
static char *screentitle;

long amiga_command_key_code = 0x5f;	/* the "HELP" key */
short amiga_winsizes[4] = {0, 8, 640, 180};
short amiga_winmode = 0;

struct WND_session {
	char *window_title;
	struct session *sp;
	struct Window *w;
	struct IOStdReq *consiorq;
} WND_sessions[NSESSIONS];


int
init_windows(void)
{
	char title[100];

	IntuitionBase =
		(struct IntuitionBase *)OpenLibrary("intuition.library", 31L);
	if (IntuitionBase == (struct IntuitionBase *)NULL)
		return 0;
	if ((WND_msgport = CreatePort(0,0)) == (struct MsgPort *)NULL) {
		CloseLibrary(&IntuitionBase->LibNode);
		return 0;
	}
	if ((DOIO_msgport = CreatePort(0,0)) == (struct MsgPort *)NULL) {
		CloseLibrary(&IntuitionBase->LibNode);
		DeletePort(WND_msgport);
		return 0;
	}
	if (OpenDevice("console.device", -1,
			(struct IORequest *)&cd_ioreq, 0) != 0L) {
		CloseLibrary(&IntuitionBase->LibNode);
		DeletePort(WND_msgport);
		DeletePort(DOIO_msgport);
		return 0;
	}
	ConsoleDevice = (struct Library *) cd_ioreq.io_Device;

	sprintf(title,
	   "KA9Q NOS v%s by Phil Karn [Amiga port by Louis Mamakos, WA3YMH]",
				Version);
	screentitle = strdup(title);
	return NSESSIONS;
}

/*
 * called from iostop(), when NET/NOS is about to exit.  Tasking has already
 * been suspended
 */
void
term_windows(void)
{
	register int i;

	/* first, close all of the windows */
	for(i = 0; i < NSESSIONS; i++) {
		struct WND_session *wnd = &WND_sessions[i];
		if (wnd->w != (struct Window *)NULL)
			freescreen(wnd->sp);
	}
	CloseDevice((struct IORequest *)&cd_ioreq);
	CloseLibrary(&IntuitionBase->LibNode);
	DeletePort(WND_msgport);
}



/* Called when a new session is created; prepares a new logical display
   screen for this session.  Returns non-zero if successful */
struct screen *
newscreen(sp)
struct session *sp;
{
	char title[80];
	static struct NewWindow nw = {
		0, 0, 0, 0,		/* leftedge, topedge, width, height */
		3, 1,			/* detail pen, block pen */
		0,			/* IDCMP flags */
		WINDOWSIZING | WINDOWDEPTH | WINDOWDRAG | /* flags */
		SMART_REFRESH | NOCAREREFRESH,
		NULL, NULL,		/* gadget list, checkmark */
		NULL, NULL,		/* title (filled in later), screen */
		NULL,			/* bitmap */
		100, 40, ~0, ~0,	/* min width, height, max width,height */
		WBENCHSCREEN
	};
	int i;
	register struct WND_session *wnd;

	/* first: find a free WND structure for this session */
	for (wnd = &WND_sessions[0]; wnd < &WND_sessions[NSESSIONS]; wnd++)
		if (wnd->w == (struct Window *)NULL)
			break;
	if (wnd == &WND_sessions[NSESSIONS]) {
		printf("PANIC! can't find free WND!\n");
		exit(1);
	}

	i = (int)(sp - Sessions);
	/* second: build the title for the window */
	if (sp->name) {
		(void)sprintf(title,"NOS %s Session %d: ",
			Sestypes[sp->type], i);
		strncat(title, sp->name, sizeof(title) - 1 - strlen(title));
	} else 
		(void)sprintf(title,"NOS Session %d: (no name)", i);

	if ((wnd->window_title = strdup(title)) == NULL)
		return NULLSCREEN;	/* sorry, no memory */

	/* now: open a new window */
	nw.Title = wnd->window_title;
	nw.LeftEdge = amiga_winsizes[0];
	nw.TopEdge  = amiga_winsizes[1];
	nw.Width    = amiga_winsizes[2];
	nw.Height   = amiga_winsizes[3];

	if ((wnd->w = OpenWindow(&nw)) == (struct Window *)NULL) {
		free(wnd->window_title);
		wnd->window_title = NULL;
		printf("Can't open window\n");
		return NULLSCREEN;
	}
	wnd->w->UserPort = WND_msgport;
	ModifyIDCMP(wnd->w, ACTIVEWINDOW | MENUPICK | CLOSEWINDOW | RAWKEY);
	SetWindowTitles(wnd->w, (char *)-1, screentitle);

	/* now: prepare an I/O request to open a console.device in the window */
	if ((wnd->consiorq = CreateStdIO(DOIO_msgport)) == NULL) {
		ModifyIDCMP(wnd->w, 0);
		CloseWindowSafely(wnd->w);
		wnd->w = (struct Window *)NULL;
		free(wnd->window_title);
		wnd->window_title = NULL;
		printf("Can't create iorq\n");
		return 0;
	}

	/* open the console handler in the window.. */
	wnd->consiorq->io_Data = (APTR)wnd->w;
	if (OpenDevice("console.device", 0,
			(struct IORequest *) wnd->consiorq, 0) != 0L) {
		DeleteStdIO(wnd->consiorq);
		ModifyIDCMP(wnd->w, 0);
		CloseWindowSafely(wnd->w);
		wnd->w = (struct Window *)NULL;
		free(wnd->window_title);
		wnd->window_title = NULL;
		printf("Can't open console.device");
		return 0;
	}

	/* finally, link the two structures together */
	wnd->sp = sp;
	wnd->w->UserData = (BYTE *)wnd;
	sp->screen = (struct screen *)wnd;

	return (struct screen *)wnd;
}

/* Free logical screen associated with the session */
void
freescreen(sp)
struct session *sp;
{
	register struct WND_session *wnd = (struct WND_session *)sp->screen;

	/* I know that I can always do this since all the I/O done with this
	   is synchronous */
	conflsh();
	CloseDevice((struct IORequest *)wnd->consiorq);
	DeleteStdIO(wnd->consiorq);
	wnd->consiorq = (struct IOStdReq *)NULL;
	CloseWindowSafely(wnd->w);
	wnd->w = (struct Window *)NULL;
	free(wnd->window_title);
	wnd->window_title = NULL;
	wnd->sp = (struct session *)NULL;
}

static void
CloseWindowSafely(w)
struct Window *w;
{
	register struct IntuiMessage *im, *next;

	if(w == (struct Window *) NULL) {
		return;
	}

	Forbid();	/* *** */
	for(im = (struct IntuiMessage *)w->UserPort->mp_MsgList.lh_Head;
	    next = (struct IntuiMessage *)im->ExecMessage.mn_Node.ln_Succ;
	    im = next)
		if (im->IDCMPWindow == w) {
			Remove(&im->ExecMessage.mn_Node);
			ReplyMsg(&im->ExecMessage);
		}

	w->UserPort = (struct MsgPort *)NULL;
	ModifyIDCMP(w, 0L);
	Permit();	/* *** */

	CloseWindow(w);
}



/* swap the "active" session to a different screen/window */

void
swapscreen(old,new)
struct session *old,*new;
{
	if(old == new)
		return;	/* Nothing to do */

	if (new) {
		register struct WND_session *wnd = 
					(struct WND_session *) new->screen;
		if (wnd->w) {
			ActivateWindow(wnd->w);
			WindowToFront(wnd->w);
		}
	}
	alert(Display,1);	/* Wake him up */
}


#define CONPUTC(wnd,c) {char ch = c; \
			(wnd)->consiorq->io_Command = CMD_WRITE; \
			(wnd)->consiorq->io_Data = (APTR)&ch; \
			(wnd)->consiorq->io_Length = 1; \
			DoIO((struct IORequest *)(wnd)->consiorq);}

#define CONPUTS(wnd,s) {conflsh(); \
			(wnd)->consiorq->io_Command = CMD_WRITE; \
			(wnd)->consiorq->io_Data = (APTR)s; \
			(wnd)->consiorq->io_Length = -1L; \
			DoIO((struct IORequest *)(wnd)->consiorq);}

#define	CONBLEN	80
static char conbuf[CONBLEN];
static char *conbp = conbuf;
static struct WND_session *consess = NULL;

void
conflsh(void)
{
	if ((conbp == conbuf) || (consess == NULL)
			|| (consess->consiorq == NULL))
		return;

	consess->consiorq->io_Command = CMD_WRITE;
	consess->consiorq->io_Data = (APTR)conbuf;
	consess->consiorq->io_Length = conbp - conbuf;
	DoIO((struct IORequest *)consess->consiorq);
	conbp = conbuf;
}

void
conputc(wnd, c)
struct WND_session *wnd;
char c;
{
	if (wnd != consess)
		conflsh();
	consess = wnd;
	*conbp++ = c;
	if ((c == '\n') || (conbp == &conbuf[CONBLEN]))
		conflsh();
}

/* This is the display task.  It sucks output from the "active" session and
   displays it in the appropriate window. */

void
display(i, v1, v2)
int i;
void *v1, *v2;
{
	int c;
	register struct session *sp;
	register struct WND_session *wnd;

	for(;;) {
		sp = Current;
		wnd = (struct WND_session *) sp->screen;
		if (sp->morewait) {
			pwait(&sp->row);
			if(sp != Current || sp->row <= 0){
				/* Current changed value, or the user
				 * hasn't really hit a key
				 */
				continue;
			}
			/* Erase the prompt */
			CONPUTS(wnd, "\r        \r");
		}
		sp->morewait = 0;
		if((c = recvchar(sp->output)) == -1){
			/* the alert() in swapscreen will cause this to
			 * return -1 when current changes
			 */
			conflsh();
			pwait(NULL);	/* Prevent a nasty loop */
			continue;
		}
		if (wnd->w == (struct Window *)NULL)
			continue;

		if (sp->record != NULLFILE) {
			if (c != '\r')
				fputc(c, sp->record);
		}
		conputc(wnd, (char)c);
		if(sp->flowmode && c == '\n' && --sp->row <= 0){
			CONPUTS(wnd,"--More--");
			sp->morewait = 1;
		}
	}
}

/*
 *  This routine is called from the amiga.c module whenever a message arrives
 *  on the shared intuition message port for all of our windows.
 */
void
check_WND_event(void)
{
	struct IntuiMessage *im;
	struct WND_session *wnd;
	static struct InputEvent FakedEvent = {NULL, IECLASS_RAWKEY, 0, 0, 0};
	char keybuf[20];

	while (im = (struct IntuiMessage *)GetMsg(WND_msgport)) {
		wnd = (struct WND_session *)im->IDCMPWindow->UserData;
		if (wnd->w != im->IDCMPWindow) {
			printf("PANIC! IntuiMessage window pointer busted\n");
			exit(-2);
		}
		switch (im->Class) {

		case ACTIVEWINDOW:
#if	0
			if ((wnd->w == (struct Window *)NULL) ||
			    (wnd != (struct WND_session *)wnd->sp->screen.save))
				break;

			if (wnd->sp == Current)
				break;

			conflsh();

			ttysetmode(wnd->sp->ttymode);
			swapscreen(Current, wnd->sp);
			Current = wnd->sp;
			psignal(Current, 0);
#endif
			break;

		case RAWKEY:
			if (wnd->w == (struct Window *)NULL)
				break;

			/* test for HELP key which is turned into -2 for the
			   escape key */
			if (im->Code == amiga_command_key_code) {
				KBQCHAR(-2);
			} else {
				short int i, keylen;

				FakedEvent.ie_Code = im->Code;
				FakedEvent.ie_Qualifier = im->Qualifier;
				FakedEvent.ie_EventAddress = *((APTR *)im->IAddress);
				keylen = (short int) RawKeyConvert(&FakedEvent, 
					   keybuf, (LONG) sizeof(keybuf), NULL);

				for(i = 0; i < keylen; i++)
					KBQCHAR(keybuf[i]);
			}
			psignal(&Keyboard, 1);
			break;
		}
		ReplyMsg(&im->ExecMessage);
	}
}

