#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include "MiniTerm.h"
#include "MiniTerm.i"

#define Buf_Size 	1024
#define Chat_Size 	 128

struct Screen *TermScreen = NULL;
struct Window *TermWindow = NULL;
struct Window *ChatWindow = NULL;

struct IOExtSer 		*SerReadIO 		= NULL;
struct IOExtSer 		*SerWriteIO 	= NULL;

struct IOStdReq 		*ConReadIO		= NULL;
struct IOStdReq		    *ConWriteIO  	= NULL;
struct IOStdReq			*ConWriteIO_Chat= NULL;

struct MsgPort 			*TimerMP 		= NULL;
struct MsgPort 			*SerReadMP 		= NULL;
struct MsgPort 			*SerWriteMP 	= NULL;
struct MsgPort 			*ConReadMP		= NULL;
struct MsgPort 			*ConWriteMP		= NULL;
struct MsgPort			*ConWriteMP_Chat= NULL;

struct timerequest 		*TimerIO		= NULL;
struct timeval 			*TimerVal		= NULL;

struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;

ULONG WindowSig, WindowSig_Chat, SerReadSig, ConReadSig, TimerSig, AllSigs;
BYTE TitleBar = 1, SerialOpen, ConsoleOpen, ConsoleOpen_Chat, TimerOpen;
BYTE SerialOn, ConsoleOn, TimerOn, SerialByte, ConsoleByte, CursorOn;
BYTE *Buffer, *ChatBuffer, ChatOn = 0, ChatCounter;

VOID _main(VOID)
{
	ULONG signal;
	SHORT Actual;
	BYTE SB, CB;
	
	OpenIntuition();
	CreatePorts();
	CreateIOs();
	OpenMemory();
	OpenDevices();
	
	AllSigs = ConReadSig | WindowSig | WindowSig_Chat | TimerSig;

	QueueConRead(&ConsoleByte);
	
	ConsoleOn = 1;
	
	FOREVER {
		signal = Wait(AllSigs);
		if(signal & WindowSig)
			HandleIDCMP(0);
		if(signal & WindowSig_Chat)
			HandleIDCMP(1);
		if(signal & ConReadSig)
			if(CB = ConGetChar(&ConsoleByte)){
				if(!ChatOn){
					if(SerialOn)
						SerPutChar(CB);
					else
						ConPutChar(CB);
				}
				else{
					if(CB == '\b'){
						if(ChatCounter > 0){
							ChatCounter--;
							ChatPutString("\b \b");
						}
					}
					else if(CB == 24){
						ChatPutChar(12);
						ChatCounter = 0;
					}
					else if(CB == '\r'){
						ChatPutChar(12);
						ChatBuffer[ChatCounter++] = '\r';
						ChatBuffer[ChatCounter] = 0;
						if(SerialOn)
							SerPutString(ChatBuffer);
						else
							ConPutString(ChatBuffer);
						ChatCounter = 0;
					}
					else if(ChatCounter != Chat_Size){
						ChatPutChar(CB);
						ChatBuffer[ChatCounter++] = CB;
					}
				}	
			}

		if(signal & SerReadSig){
			if(SB = SerGetChar(&SerialByte)){
				ConPutChar(SB);
				if(Actual = CheckActual()){
					if(Actual > (Buf_Size-1))
						Actual = Buf_Size - 1;
					SerGetBuffer(Buffer, Actual);
					ConPutBuffer(Buffer, Actual);
				}
			}
			QueueSerRead(&SerialByte);
		}
	}
}

VOID ChatPutChar(BYTE byte)
{
	ConWriteIO_Chat->io_Command = CMD_WRITE;
	ConWriteIO_Chat->io_Data = (APTR) &byte;
	ConWriteIO_Chat->io_Length = 1;
 	DoIO(ConWriteIO_Chat);
}

VOID ChatPutString(BYTE *string)
{
	ConWriteIO_Chat->io_Command = CMD_WRITE;
	ConWriteIO_Chat->io_Data = (APTR) string;
	ConWriteIO_Chat->io_Length = -1;
	DoIO(ConWriteIO_Chat);
}

VOID ToggleCursor(BYTE on)
{
	if(on == 1){
		ConPutString("[ p");
		CursorOn = 1;
	}
	else if(on == 0){
		ConPutString("[0 p");
		CursorOn = 0;
	}
	else if(on == 2){
		if(CursorOn == 1)
			ToggleCursor(0);
		else
			ToggleCursor(1);
	}
}

VOID OpenIntuition(VOID)
{
	if(!(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",33)))
		CleanExit();
	
	if(!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",33)))
		CleanExit();
		
	TermNewScreen.Width = GfxBase->NormalDisplayColumns;
	TermNewScreen.Height = GfxBase->NormalDisplayRows;
	TermNewWindow.Width = TermNewScreen.Width;
	TermNewWindow.Height = TermNewScreen.Height;
	
	CloseLibrary(GfxBase);
	
	if(!(TermScreen = (struct Screen *) OpenScreen(&TermNewScreen)))
		CleanExit();
	
	TermNewWindow.Screen = TermScreen;
	
	TermNewWindow_Chat.Screen = TermScreen;
	TermNewWindow_Chat.Width = TermScreen->Width;
	TermNewWindow_Chat.Height = 21;
	TermNewWindow_Chat.TopEdge = TermScreen->Height-21;
	
	if(!(ChatWindow = (struct Window *) OpenWindow(&TermNewWindow_Chat)))
		CleanExit();
	
	if(!(TermWindow = (struct Window *) OpenWindow(&TermNewWindow)))
		CleanExit();
		
	SetMenuStrip(TermWindow, &TermMenu);
	
	WindowSig = 1 << TermWindow->UserPort->mp_SigBit;
	WindowSig_Chat = 1 << ChatWindow->UserPort->mp_SigBit;
}

VOID CreatePorts(VOID)
{
	if(!(TimerMP = (struct MsgPort *) CreatePort(0,0)))
		CleanExit();
	
	if(!(ConReadMP = (struct MsgPort *) CreatePort("WK_Console_Read_I",0)))
		CleanExit();
	
	if(!(ConWriteMP = (struct MsgPort *) CreatePort("WK_Console_Write_I",0)))
		CleanExit();
	
	if(!(ConWriteMP_Chat = (struct MsgPort *) CreatePort("WK_Console_Write_II",0)))
		CleanExit();
}	

VOID CreateIOs(VOID)
{
	if(!(TimerIO = (struct timerequest *) CreateExtIO(TimerMP, sizeof(struct timerequest))))
		CleanExit();
	
	if(!(ConReadIO = (struct IOStdReq *) CreateExtIO(ConReadMP, (LONG) sizeof(struct IOStdReq))))
		CleanExit();
	
	if(!(ConWriteIO = (struct IOStdReq *) CreateExtIO(ConWriteMP, (LONG) sizeof(struct IOStdReq))))
		CleanExit();

	if(!(ConWriteIO_Chat = (struct IOStdReq *) CreateExtIO(ConWriteMP_Chat, (LONG) sizeof(struct IOStdReq))))
		CleanExit();
}

VOID OpenDevices(VOID)
{
	if(OpenConsole())
		CleanExit();
				
	if(OpenTimer())
		CleanExit();	
}

VOID OpenMemory(VOID)
{
	if(!(TimerVal = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
		CleanExit();
	
	if(!(Buffer = (BYTE *) AllocMem(Buf_Size, MEMF_PUBLIC)))
		CleanExit();
	
	if(!(ChatBuffer = (BYTE *) AllocMem(Chat_Size, MEMF_PUBLIC)))
		CleanExit();
}

VOID CleanExit(VOID)
{	
	if(SerialOpen)					CloseSerial();
	if(ConsoleOpen)					CloseConsole();
	if(TimerOpen)					CloseTimer();
	
	if(ConReadIO)					DeleteExtIO(ConReadIO);
	if(ConWriteIO)					DeleteExtIO(ConWriteIO);
	if(ConWriteIO_Chat)				DeleteExtIO(ConWriteIO_Chat);
	if(SerReadIO)					DeleteExtIO(SerReadIO);
	if(SerWriteIO)					DeleteExtIO(SerWriteIO);
	
	if(TimerIO)						DeleteExtIO(TimerIO);
	
	if(ConReadMP)					DeletePort(ConReadMP);
	if(ConWriteMP)					DeletePort(ConWriteMP);
	if(ConWriteMP_Chat)				DeletePort(ConWriteMP_Chat);	
	if(SerReadMP)					DeletePort(SerReadMP);
	if(SerWriteMP)					DeletePort(SerWriteMP);

	if(TimerMP)						DeletePort(TimerMP);
	if(TimerVal)					FreeMem(TimerVal, sizeof(struct timeval));
	if(Buffer)						FreeMem(Buffer, Buf_Size);
	if(ChatBuffer)					FreeMem(ChatBuffer, Chat_Size);
	if(TermWindow){
		if(TermWindow->MenuStrip)	ClearMenuStrip(TermWindow);
									CloseWindow(TermWindow);
	}
	if(ChatWindow)					CloseWindow(ChatWindow);
	if(TermScreen)					CloseScreen(TermScreen);
	if(IntuitionBase)				CloseLibrary(IntuitionBase);
	exit(0);
}

VOID HandleIDCMP(BYTE wnd)
{
	struct IntuiMessage *Message;
	ULONG Class;
	USHORT Code;
	
	BYTE MenuNum, ItemNum;
	
	while(Message = (struct IntuiMessage *) ((wnd == 0) ? GetMsg(TermWindow->UserPort) : GetMsg(ChatWindow->UserPort))){
		Class = Message->Class;
		Code = Message->Code;
		ReplyMsg(Message);
		
		switch(Class){
			case ACTIVEWINDOW:
				ActivateWindow(TermWindow);
				break;

			case MENUPICK:
				MenuNum = MENUNUM(Code);
				ItemNum = ITEMNUM(Code);
			
				switch(ItemNum){
					case 0:
						if(TitleBar){
							ShowTitle(TermScreen, FALSE);
							TitleBar = 0;
						}
						else{
							ShowTitle(TermScreen, TRUE);
							TitleBar = 1;
						}
						break;
					case 1:
						if(SerialOpen){
							CloseSerial();
							SerialOpen = 0;
							ClearMenuStrip(TermWindow);
							ProjectText[1].IText = "Open Serial";
							SetMenuStrip(TermWindow, &TermMenu);
						}
						else{
							if(!(OpenSerial())){
								SerialOpen = 1;
								ClearMenuStrip(TermWindow);
								ProjectText[1].IText = "Close Serial";
								SetMenuStrip(TermWindow, &TermMenu);
							}
							else
								ConPutString("Error opening the serial port.");
						}
						break;
					case 2:
						if(ChatOn){
							ChatOn = 0;
							ClearMenuStrip(TermWindow);
							ProjectText[2].IText = "Open Chat";
							SetMenuStrip(TermWindow, &TermMenu);
							SizeWindow(TermWindow, 0, 22);
						}
						else {
							ChatOn = 1;
							ClearMenuStrip(TermWindow);
							ProjectText[2].IText = "Close Chat";
							SetMenuStrip(TermWindow, &TermMenu);
							SizeWindow(TermWindow, 0, -22);
						}
							break;
					case 3:
						ChatPutString("[0;31;40m");
						ConPutString("[0;31;40m");
						break;
					case 4:
						CleanExit();
						break;
				}
		}
	}
}	
	
VOID CloseConsole(VOID)
{
	if(ConsoleOn){
		if(!(CheckIO(ConReadIO)))	AbortIO(ConReadIO);
		WaitIO(ConReadIO);
	}
	if(ConsoleOpen_Chat)	CloseDevice(ConWriteIO_Chat);
	
	CloseDevice(ConWriteIO);
}

VOID CloseSerial(VOID)
{
	
	if(SerialOn){
		if(!(CheckIO(SerReadIO))) AbortIO(SerReadIO);
		WaitIO(SerReadIO);
	}
	SerialOn = 0;
	AllSigs &= ~SerReadSig;
	ClearSerial();
	CloseDevice(SerWriteIO);
	
	if(SerReadIO){
		DeleteExtIO(SerReadIO);
		SerReadIO = NULL;
	}
	if(SerWriteIO){
		DeleteExtIO(SerWriteIO);
		SerWriteIO = NULL;
	}
	if(SerReadMP){
		DeletePort(SerReadMP);
		SerReadMP = NULL;
	}
	if(SerWriteMP){
		DeletePort(SerWriteMP);
		SerWriteMP = NULL;
	}
}

VOID CloseTimer(VOID)
{
	if(TimerOn){
	if(!(CheckIO(TimerIO)))	AbortIO(TimerIO);
		WaitIO(TimerIO);
	}
	CloseDevice(TimerIO);
}

ULONG CheckActual(void)
{
	SerReadIO->IOSer.io_Command = SDCMD_QUERY;
	DoIO(SerReadIO);
	
	return SerReadIO->IOSer.io_Actual;
}

VOID CXBRK(VOID){return;}

BYTE CheckCD(VOID)
{
	SerWriteIO->IOSer.io_Command = SDCMD_QUERY;
	DoIO(SerWriteIO);
	if(SerWriteIO->io_Status & 040)
		return 0;
	else
		return 1;
}

BYTE CheckCTS(VOID)
{
	SerWriteIO->IOSer.io_Command = SDCMD_QUERY;
	DoIO(SerWriteIO);
	if(SerWriteIO->io_Status & 020)
		return 0;
	else
		return 1;
}

VOID ClearSerial(VOID)
{
	SerReadIO->IOSer.io_Command = CMD_CLEAR;
	DoIO(SerReadIO);
}

BYTE OpenConsole(VOID)	
{
	BYTE error;
	
	ConWriteIO->io_Data = (APTR) TermWindow;
	ConWriteIO->io_Length = sizeof(struct Window);
	
	error = OpenDevice("console.device", 0, ConWriteIO, 0);
	
	ConReadIO->io_Device = ConWriteIO->io_Device;
	ConReadIO->io_Unit   = ConWriteIO->io_Unit;
	if(!error){
		ConsoleOpen = 1;
		ConReadSig = 1 << ConReadMP->mp_SigBit;
		
		ConWriteIO_Chat->io_Data = (APTR) ChatWindow;
		ConWriteIO_Chat->io_Length = sizeof(struct Window);
		error = OpenDevice("console.device", 0, ConWriteIO_Chat, 0);
		if(!error)
			ConsoleOpen_Chat = 1;
	}
	return error;
}

BYTE OpenSerial(VOID)
{
	BYTE error;

	if(!(SerReadMP = (struct MsgPort *) CreatePort(0,0)))
		CleanExit();
	
	if(!(SerWriteMP = (struct MsgPort *) CreatePort(0,0)))
		CleanExit();
	
	if(!(SerReadIO = (struct IOExtSer *) CreateExtIO(SerReadMP, (LONG) sizeof(struct IOExtSer))))
		CleanExit();
	
	if(!(SerWriteIO = (struct IOExtSer *) CreateExtIO(SerWriteMP, (LONG) sizeof(struct IOExtSer))))
		CleanExit();
		
	SerWriteIO->io_SerFlags = SERF_SHARED;
	
	error = OpenDevice("serial.device", 0, SerWriteIO, 0);
	
	SerReadIO->IOSer.io_Device = SerWriteIO->IOSer.io_Device;
	SerReadIO->IOSer.io_Unit   = SerWriteIO->IOSer.io_Unit;
	
	SerReadIO->io_SerFlags = SERF_SHARED;
	
	if(!error){
		SerialOpen = 1;
		SerReadSig = 1 << SerReadMP->mp_SigBit;
		SerialOn = 1;
		ClearSerial();
		QueueSerRead(&SerialByte);
		AllSigs |= SerReadSig;
	}
	
	return error;
}

BYTE OpenTimer(VOID)
{
	BYTE error;
	
	error = OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) TimerIO, 0);
	if(!error){
		TimerOpen = 1;
		TimerSig  = 1 << TimerMP->mp_SigBit;
	}
	TimerVal->tv_secs = 1;
	TimerVal->tv_micro = 0;
	TimerIO->tr_node.io_Command = TR_ADDREQUEST;
	TimerIO->tr_time = *TimerVal;
	
	return error;
}

VOID QueueTimer(SHORT secs)
{
	TimerVal->tv_secs = secs;
	SendIO((struct IORequest *) TimerIO);
}

VOID ConPutString(BYTE *string)
{
	ConWriteIO->io_Command = CMD_WRITE;
	ConWriteIO->io_Data = (APTR) string;
	ConWriteIO->io_Length = -1;
	DoIO(ConWriteIO);
}

VOID ConPutBuffer(BYTE *buffer, SHORT Length)
{
	ConWriteIO->io_Command = CMD_WRITE;
	ConWriteIO->io_Data = (APTR) buffer;
	ConWriteIO->io_Length = Length;
	DoIO(ConWriteIO);
}

VOID ConPutChar(BYTE byte)
{
	ConWriteIO->io_Command = CMD_WRITE;
	ConWriteIO->io_Data = (APTR) &byte;
	ConWriteIO->io_Length = 1;
	DoIO(ConWriteIO);
}

VOID SerPutChar(BYTE byte)
{
	SerWriteIO->IOSer.io_Command = CMD_WRITE;
	SerWriteIO->IOSer.io_Data = (APTR) &byte;
	SerWriteIO->IOSer.io_Length = 1;
	DoIO(SerWriteIO);
}

VOID SerPutString(BYTE *string)
{
	SerWriteIO->IOSer.io_Command = CMD_WRITE;
	SerWriteIO->IOSer.io_Data = (APTR) string;
	SerWriteIO->IOSer.io_Length = -1;
	DoIO(SerWriteIO);
}

VOID QueueConRead(BYTE *WhereTo)
{
	ConReadIO->io_Command = CMD_READ;
	ConReadIO->io_Data = (APTR) WhereTo;
	ConReadIO->io_Length = 1;
	SendIO(ConReadIO);
}

VOID QueueSerRead(BYTE *WhereTo)
{
	SerReadIO->IOSer.io_Command = CMD_READ;
	SerReadIO->IOSer.io_Data = (APTR) WhereTo;
	SerReadIO->IOSer.io_Length = 1;
	SendIO(SerReadIO);
}

VOID SerGetBuffer(BYTE *buffer, SHORT Length)
{
	SerReadIO->IOSer.io_Command = CMD_READ;
	SerReadIO->IOSer.io_Data = (APTR) buffer;
	SerReadIO->IOSer.io_Length = Length;
	DoIO(SerReadIO);
}

BYTE ConGetChar(BYTE *whereto)
{
	BYTE temp;
	struct IOStdReq *readreq;
	
	if(!(readreq = (struct IOStdReq *) GetMsg(ConReadMP))) return 0;
	temp = *whereto;
	QueueConRead(whereto);
	return(temp);
}

BYTE SerGetChar(BYTE *whereto)
{
	BYTE temp;
	struct IOStdSer *readreq;
	
	if(!(readreq = (struct IOStdReq *) GetMsg(SerReadMP))) return 0;
	temp = *whereto;
	return(temp);
}
