/* SerialKey - Keyboard input over serial
 * Copyright (C) 1998 J.Ross Nicoll
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <exec/types.h>

#include <dos/dos.h>
#include <exec/execbase.h>
#include <exec/lists.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/serial.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>

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

#include <stdio.h>
#include <stdlib.h>

#define SERIAL_BUFFER_LENGTH 10240
#define SERIAL_SPEED 19200

/* Devices */
struct MsgPort *InputPort = NULL;
struct IOStdReq *InputIOReq = NULL;
struct MsgPort *SerialReadPort = NULL;
struct IOExtSer *SerialReadIOReq = NULL;
struct MsgPort *SerialWritePort = NULL;
struct IOExtSer *SerialWriteIOReq = NULL;
struct InputEvent InputEvent;
UWORD SerialDataIn;

/* Intuition */
struct IntuiMessage *MainWinMessage;
struct Window *MainWindow;

/* Libraries */
struct GfxBase *GfxBase = NULL;
struct IntuionBase *IntuitionBase = NULL;
extern struct ExecBase *SysBase;

/* Other */
BOOL InputOpened = FALSE;
BOOL SerialReadOpened = FALSE;
BOOL SerialWriteOpened = FALSE;
const TEXT version[] = {"$VER: SerialKey 1.4 (26.7.98)"};
ULONG IntuitionMsgBit;
ULONG SerialMsgBit;

/* Function prototypes */

BOOL Init(VOID);
BOOL OpenInput(VOID);
BOOL OpenSerial(VOID);
BOOL OpenSerialKeyWindow(VOID);
int main(int argc, char *argv[]);
VOID Cleanup(VOID);

/* Open libraries, devices, and set up a few things */
BOOL Init(VOID)
{
	if(SysBase -> LibNode.lib_Version >= 36)
	{
		GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 36);
		if(GfxBase != NULL)
		{
			IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36);
			if(IntuitionBase != NULL)
			{
				if(OpenInput() != FALSE)
				{
					if(OpenSerial() != FALSE)
					{
						if(OpenSerialKeyWindow() != FALSE)
						{
							IntuitionMsgBit = (1 << MainWindow -> UserPort -> mp_SigBit);
							SerialMsgBit = (1 << SerialReadIOReq -> IOSer.io_Message.mn_ReplyPort -> mp_SigBit);

							InputIOReq -> io_Command = IND_WRITEEVENT;
							InputIOReq -> io_Length = sizeof(struct InputEvent);
							InputIOReq -> io_Data = (APTR)&InputEvent;

							SerialReadIOReq -> IOSer.io_Length = 4;
							SerialReadIOReq -> IOSer.io_Command = CMD_READ;
							SerialReadIOReq -> IOSer.io_Data = (APTR)&SerialDataIn;
							SendIO((struct IORequest *)SerialReadIOReq);

							SerialWriteIOReq->IOSer.io_Length = 4;
							SerialWriteIOReq->IOSer.io_Command = CMD_WRITE;

							return(TRUE);
						}
							else
						{
							PutStr("Unable to open window.\n");
						}
					}
					else
					{
						PutStr("Unable to open serial.device.\n");
					}
				}
			}
			else
			{
				PutStr("Unable to open intuition.library version 36 or higher.\n");
			}
		}
		else
		{
			PutStr("Unable to open graphics.library version 36 or higher.\n");
		}
	}
	else
	{
		Write(Output(), "You need Kickstart V36 or higher to run SerialKey.\n", 51);
	}


	return(FALSE);
}

/* ---------------------------------------------------------------------- */	

int main(int argc, char *argv[])
{
	BOOL end = FALSE;
	UBYTE count = 0;
	ULONG trigger = 0;

	if(Init() != FALSE)
	{
		while(end == FALSE)
		{
			trigger = Wait(IntuitionMsgBit | SerialMsgBit);
			if((trigger & IntuitionMsgBit) == IntuitionMsgBit)
			{
				MainWinMessage = (struct IntuiMessage *)GetMsg(MainWindow->UserPort);

				while(MainWinMessage != NULL)
				{
					if(MainWinMessage -> Class == IDCMP_CLOSEWINDOW) end = TRUE;

					if(MainWinMessage -> Class == IDCMP_RAWKEY)
					{
						SerialWriteIOReq->IOSer.io_Data = (APTR)&MainWinMessage->Code;
						DoIO((struct IORequest *)SerialWriteIOReq);
						SerialWriteIOReq->IOSer.io_Data = (APTR)&MainWinMessage->Qualifier;
						DoIO((struct IORequest *)SerialWriteIOReq);
					}
					ReplyMsg((struct Message *)MainWinMessage);
					MainWinMessage = (struct IntuiMessage *)GetMsg(MainWindow->UserPort);
				}
			}

			if(CheckIO((struct IORequest *)SerialReadIOReq) != FALSE)
			{
				InputEvent.ie_Prev2DownCode = InputEvent.ie_Prev1DownCode;
				InputEvent.ie_Prev2DownQual = InputEvent.ie_Prev1DownQual;
				InputEvent.ie_Prev1DownCode = (UBYTE)InputEvent.ie_Code;
				InputEvent.ie_Prev1DownQual = (UBYTE)InputEvent.ie_Qualifier;
				InputEvent.ie_Code = SerialDataIn;
				DoIO((struct IORequest *)SerialReadIOReq);
				InputEvent.ie_Qualifier = SerialDataIn;
				DoIO((struct IORequest *)InputIOReq);

				SendIO((struct IORequest *)SerialReadIOReq);
			}
		}
	}

	Cleanup();

	exit(0);
}

/* ---------------------------------------------------------------------- */	

/* Cleanup before exit */
void Cleanup()
{
	if((SerialReadOpened == TRUE) &&
		(CheckIO((struct IORequest *)SerialReadIOReq) == FALSE))
	{
		AbortIO((struct IORequest *)SerialReadIOReq);
		WaitIO((struct IORequest *)SerialReadIOReq);
	}

	if(GfxBase != NULL)
	{
		CloseLibrary((struct Library *)GfxBase);

		if(IntuitionBase != NULL)
		{
			CloseLibrary((struct Library *)IntuitionBase);

			if(InputPort != NULL)
			{
				if(InputIOReq != NULL)
				{
					if(InputOpened == TRUE)
					{
						if(SerialReadPort != NULL)
						{
							if(SerialReadIOReq != NULL)
							{
								if(SerialReadOpened == TRUE)
								{
									if(SerialWritePort != NULL)
									{
										if(SerialWriteIOReq != NULL)
										{
											if(SerialWriteOpened == TRUE)
											{
												if(MainWindow != NULL) CloseWindow(MainWindow);

												CloseDevice((struct IORequest *)SerialWriteIOReq);
											}
											DeleteExtIO((struct IORequest *)SerialWriteIOReq);
										}
										DeletePort(SerialWritePort);
									}
									CloseDevice((struct IORequest *)SerialReadIOReq);
								}
								DeleteExtIO((struct IORequest *)SerialReadIOReq);
							}
							DeletePort(SerialReadPort);
						}
						CloseDevice((struct IORequest *)InputIOReq);
					}
					DeleteExtIO((struct IORequest *)InputIOReq);
				}
				DeletePort(InputPort);
			}
		}
	}

	return();
}

/* ---------------------------------------------------------------------- */

/* Open and setup input.device */
BOOL OpenInput()
{
	BYTE error;

	InputPort = (struct MsgPort *)CreatePort(NULL, 9);
	if(InputPort != NULL)
	{
		InputIOReq = (struct IOStdReq *)CreateExtIO(InputPort, (long)sizeof(struct IOStdReq));
		if(InputIOReq != NULL)
		{
			error = OpenDevice("input.device", 0, (struct IORequest *)InputIOReq, 0);
			if(error == 0)
			{
				InputOpened = TRUE;
				InputEvent.ie_Class = IECLASS_RAWKEY;

				return(TRUE);
			}
			else
			{
				PutStr("Unable to open input.device.\n");
			}
		}
		else
		{
			PutStr("Unable to create IO request for input.device.\n");
		}
	}
	else
	{
		PutStr("Unable to create port for input.device.\n");
	}

	return(FALSE);
}

/* ---------------------------------------------------------------------- */	

/* Open and setup serial.device */
BOOL OpenSerial()
{
	BYTE error;

	SerialReadPort = (struct MsgPort *)CreatePort(NULL, 0);
	if(SerialReadPort != NULL)
	{
		SerialReadIOReq = (struct IOExtSer *)CreateExtIO(SerialReadPort, (long)sizeof(struct IOExtSer));
		if(SerialReadIOReq != NULL)
		{
			SerialReadIOReq->io_SerFlags = SERF_SHARED;
			error = OpenDevice("serial.device", 0, (struct IORequest *)SerialReadIOReq, 0);
			if(error == 0)
			{
				SerialReadOpened = TRUE;

				SerialReadIOReq -> IOSer.io_Command = SDCMD_SETPARAMS;
				SerialReadIOReq -> io_RBufLen = SERIAL_BUFFER_LENGTH;
				SerialReadIOReq -> io_ExtFlags = 0;
				SerialReadIOReq -> io_Baud = SERIAL_SPEED;
				SerialReadIOReq -> io_ReadLen = 8;
				SerialReadIOReq -> io_WriteLen = 8;
				SerialReadIOReq -> io_StopBits = 1;
				SerialReadIOReq -> io_SerFlags = SERF_XDISABLED;
				DoIO((struct IORequest *)SerialReadIOReq);

				SerialWritePort = (struct MsgPort *)CreatePort(NULL, 0);
				if(SerialWritePort != NULL)
				{
					SerialWriteIOReq = (struct IOExtSer *)CreateExtIO(SerialWritePort, (long)sizeof(struct IOExtSer));
					if(SerialWriteIOReq != NULL)
					{
						SerialWriteIOReq->io_SerFlags = SERF_SHARED;

						error = OpenDevice("serial.device", 0, (struct IORequest *)SerialWriteIOReq, 0);
						if(error == 0)
						{
							SerialWriteOpened = TRUE;

							SerialWriteIOReq -> IOSer.io_Command = SDCMD_SETPARAMS;
							SerialWriteIOReq -> io_RBufLen = SERIAL_BUFFER_LENGTH;
							SerialWriteIOReq -> io_ExtFlags = 0;
							SerialWriteIOReq -> io_Baud = SERIAL_SPEED;
							SerialWriteIOReq -> io_ReadLen = 8;
							SerialWriteIOReq -> io_WriteLen = 8;
							SerialWriteIOReq -> io_StopBits = 1;
							SerialWriteIOReq -> io_SerFlags = SERF_XDISABLED;
							DoIO((struct IORequest *)SerialWriteIOReq);

							return(TRUE);
						}
						else
						{
							PutStr("Unable to open serial.device.\n");
						}
					}
					else
					{
						PutStr("Unable to create IO request for serial.device.\n");
					}
				}
				else
				{
					PutStr("Unable to create port for serial.device.\n");
				}
			}
			else
			{
				PutStr("Unable to open serial.device.\n");
			}
		}
		else
		{
			PutStr("Unable to create IO request for serial.device.\n");
		}
	}
	else
	{
		PutStr("Unable to create port for serial.device.\n");
	}

	return(FALSE);
}

/* ---------------------------------------------------------------------- */	

/* Open the SerialKey window. */
BOOL OpenSerialKeyWindow()
{
	int new_window_height = 0;
	int new_window_width = 0;
	struct NewWindow MainWindowDef = {
		0,0,
		1,1,

		0,1,

		IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,

		WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE,

		NULL,

		NULL,

		"Serial Keyboard",

		NULL,

		NULL,

		0,0,
		0,0,

		WBENCHSCREEN
	};

	struct NewWindow ScoutWindowDef = MainWindowDef;
	struct Window *ScoutWindow;
	ScoutWindowDef.IDCMPFlags = NULL;
	ScoutWindowDef.Flags = WFLG_BORDERLESS;
	ScoutWindowDef.Title = NULL;

	struct TextFont *ScreenFont = NULL;

	/* To get the workbench screen font, we open one window, get the screen
	 * font from that and then close it. If anyone knows a better way of doing
	 * this, I'd love to know.
	 */
	ScoutWindow = OpenWindow(&ScoutWindowDef);
	if(ScoutWindow != NULL)
	{
		ScreenFont = (struct TextFont *)OpenFont(ScoutWindow -> WScreen -> Font);
		if(ScreenFont != NULL)
		{
			MainWindowDef.Height = ScreenFont -> tf_YSize + 3;
			MainWindowDef.Width  = (ScreenFont -> tf_XSize * 15) + 62;
			CloseFont(ScreenFont);
			CloseWindow(ScoutWindow);

			MainWindow = OpenWindow(&MainWindowDef);
			if(MainWindow != NULL)
			{
				SetWindowTitles(MainWindow, MainWindow -> Title, "SerialKey Version 1.4 © 1998 J.Ross Nicoll");

				return(TRUE);
			}
			else
			{
				PutStr("Unable to open main window.\n");
			}
		}
		else
		{
			CloseWindow(ScoutWindow);
			PutStr("Unable to open screen font.\n");
		}
	}
	else
	{
		PutStr("Unable to open scout window.\n");
	}

	return(FALSE);
}
