#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/keymap.h>
#include <devices/serial.h>
#include <devices/console.h>
#include <devices/audio.h>
#include <libraries/dos.h>
#include <hardware/blit.h>
#include <graphics/sprite.h>
#include <intuition/preferences.h>
#include <intuition/intuition.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>

#include "main.h"

#include <graphics/gfxbase.h>

char infotext[] = 
    "\x1b#3\x9b""1mV A X t e r m\x9b""0m  v2.4   "
    "\x9b""3mBy T. Mickelsson\x9b""0m\x0D\n"
    "\x1b#4\x9b""1mV A X t e r m\x9b""0m  v2.4   "
    "\x9b""3mBy T. Mickelsson\x9b""0m\x0D\n"
    "\n\x1b#6\x9b""1mVMS \x9b""22moriented \x9b""1m\x9b""4m"
    "VT220 Terminal Emulator""\x9b""22m\x9b""24m\x0D\n\n"
    "\x9b""4mSpecial keys\x9b""0m\x0D\n\n"
    "\x9b""1mF1\x9b""0m		hold/release screen\x0D\n"
    "\x9b""1mF2\x9b""0m		printer on/off\x0D\n"
    "\x9b""1mF3\x9b""0m		set-up\x0D\n"
    "\x9b""1mF4\x9b""0m		file transfers/host set-up/quit\x0D\n"
    "\x9b""1mF5\x9b""0m		break\x0D\n\n"
    "Right mouse button toggles the mouse pointer and screen gadget.\x0D\n\n"
    "When the printer will be activated for the first time, "
    "file PRINTER.SETUP will\x0D\n"
    "be sent to printer to modify the default printer settings.\x0D\n\n"
    "The commands for the host set-up will be read from the "
    "HOST.SETUP file.\x0D\n\n"
    "File transfers will work only when the terminal emulator "
    "is logged into the\x0D\n"
    "VMS-system. The system must be ready to accept DCL-command from "
    "the terminal.\x0D\n\n";

struct NewScreen nscr = {
    0,0,WIDTH,0,DEPTH,
    BACKGROUND_PEN,FOREGROUND_PEN,HIRES,CUSTOMSCREEN,
    NULL,(UBYTE *)"Right mouse button to toggles this title!  "
    "Use F4 and 'Q' keys to quit!",NULL,NULL };

struct NewWindow nwin = {
    0,0,WIDTH,0,FOREGROUND_PEN,FOREGROUND_PEN,
    MOUSEBUTTONS|ACTIVEWINDOW|INACTIVEWINDOW,
    SMART_REFRESH|BACKDROP|BORDERLESS|ACTIVATE|NOCAREREFRESH|RMBTRAP,
    NULL,NULL,NULL,NULL,NULL,0,0,0,0,CUSTOMSCREEN };

struct Library *IntuitionBase;
struct SimpleSprite sp = { NULL,YSIZE,0,0,-1 };

USHORT  rows;
char    line[80],cdir[64],fdir[64];

void _main(char *argv)
{
struct GfxBase          *gb;
struct console          console,*con;
struct serial           serial,*ser;
register LONG           len;
char                    *cp;
BPTR                    fp;
USHORT                  stat;

if (*argv) {
    if (cp = strchr(argv,' ')) *cp = '\0';
    cp = argv;
    cp = cp + strlen(cp) - 1;
    while((*cp != ':')&&(*cp != '/')) {
        if (cp == argv) {
            cp--;
            break;
            }
        cp--;
        }
    cp++;
    *cp = '\0';
    getcd(0,cdir);
    chdir(argv);
    getcd(0,fdir);
    chdir(cdir);
    }
else getcd(0,fdir);
strcpy(line,"c:assign VTDIR: \"");
strcat(line,fdir);
strcat(line,"\"");
if (fp = OPEN_FOR_WRITING(NILDEVICE)) {
    Execute(line,NULL,fp);
    CLOSEFILE(fp);
    }
con = &console;
ser = &serial;
con->sp = &sp;
con->ser = ser;
if ((gb=(struct GfxBase *)OpenLibrary("graphics.library",0)) == NULL) return;
rows = gb->NormalDisplayRows / YSIZE;
nscr.Height = rows * YSIZE;
nwin.Height = rows * YSIZE;
CloseLibrary((struct Library *)gb);
if ((IntuitionBase = OpenLibrary("intuition.library",0)) != NULL) {
    if (!(openserial(ser))) {
        if (fp = OPEN_FOR_WRITING(NILDEVICE)) {
            Execute("Assign FONTS: VTDIR:fonts",NULL,fp);
            CLOSEFILE(fp);
            }
        stat = openconsole(con);
        if (fp = OPEN_FOR_WRITING(NILDEVICE)) {
            Execute("Assign FONTS: sys:fonts",NULL,fp);
            CLOSEFILE(fp);
            }
        if (!stat) {
            strcpy(con->buf,infotext);
            interpret(con,strlen(con->buf));
            readsetup(con);
            while (con->gstat & ON) {
                len = readconsole(con);
                if (len) writeserial(con,con->buf,len);
                if (!(con->gstat & HOLD)) {
                    len = readserial(con,1);
                    if (len) {
                        interpret(con,len);
                        if (con->gstat & PRINT) WRITE(con->prt,con->buf,len);
                        }
                    else cursorout(con);
                    }
                if (con->gstat & SEND_BREAK) {
                    con->gstat &= (MASK - SEND_BREAK);
                    send_break(ser);
                    }
                else if (con->gstat & TRANSFER) {
                    con->gstat &= (MASK - TRANSFER);
                    transfer(con);
                    }
                else if (con->gstat & SETUP) {
                    con->gstat &= (MASK - SETUP);
                    setup(con);
                    }
                }
            closeconsole(con);
            }
        closeserial(ser);
        }
    CloseLibrary(IntuitionBase);
    }
}

readconsole(register struct console *con)
{
register struct IOStdReq    *rreq;
register USHORT             len;
register char               *cp;
struct IntuiMessage         *mes;
ULONG                       class;
USHORT                      code;

len = 0;
if (!(con->gstat & LOCKED)) {
    if (GetMsg(con->rport) != NULL) {
        cp = con->buf;
        *cp++ = con->character;
        len = 1;
        rreq = con->rreq;
        rreq->io_Flags |= IOF_QUICK;
        while (len < (BUFSIZE - SECSIZE)) {
            BeginIO((struct IORequest *)rreq);
            if (!(rreq->io_Flags & IOF_QUICK)) return((int)len);
            *cp++ = con->character;
            len++;
            }
        }
    }
if (mes = (struct IntuiMessage *)GetMsg(con->win->UserPort)) {
    class = mes->Class;
    code = mes->Code;
    ReplyMsg((struct Message *)mes);
    switch (class) {
        case MOUSEBUTTONS:
            switch(code) {
                case MENUUP:
                    if (con->gstat & GAD) {
                        ShowTitle(con->scr,FALSE);
                        SetPointer(con->win,con->dummy,PYSIZE,PXSIZE,0,0);
                        con->gstat &= (MASK - GAD);
                        }
                    else {
                        ShowTitle(con->scr,TRUE);
                        ClearPointer(con->win);
                        con->gstat |= GAD;
                        }
                    break;
                default: ;
                }
            break;
        case INACTIVEWINDOW:
            SetRast(&con->srp,0);
            break;
        case ACTIVEWINDOW:
            initcursor(con);
            DRAWCURSOR(con->rp,&con->srp,con->row,con->col);
            break;
        default: ;
        }
    }
return((LONG)len);
}

openconsole(register struct console *con)
{
allocaudio(con);
if ((con->wport = CreatePort("console_write",0)) != NULL) {
    if ((con->wreq = CreateStdIO(con->wport)) != NULL) {
        if ((con->rport = CreatePort("console_read",0)) != NULL) {
            if ((con->rreq = CreateStdIO(con->rport)) != NULL) {
                if ((con->scr = OpenScreen(&nscr)) != NULL) {
                    ShowTitle(con->scr,FALSE);
                    nwin.Screen = con->scr;
                    if ((con->win = OpenWindow(&nwin)) != NULL) {
                        con->wreq->io_Data = (APTR)con->win;
                        con->wreq->io_Length = sizeof(*con->win);
                        if (!(OpenDevice("console.device",0,
                                        (struct IORequest *)con->wreq,0))) {
                            con->wreq->io_Command = CD_SETKEYMAP;
                            con->wreq->io_Length = sizeof(struct KeyMap);
                            con->wreq->io_Data = (APTR)&keymap;
                            DoIO((struct IORequest *)con->wreq);
                            con->wreq->io_Command = CMD_WRITE;
                            con->rreq->io_Command = CMD_READ;
                            con->rreq->io_Data = (APTR)&con->character;
                            con->rreq->io_Length = 1;
                            con->rreq->io_Device = con->wreq->io_Device;
                            con->rreq->io_Unit = con->wreq->io_Unit;
                            if (!(allocmem(con))) {
                                SetPointer(con->win,con->dummy,
                                            PYSIZE,PXSIZE,0,0);
                                con->rp = con->win->RPort;
                                if (!(openfonts(con))) {
                                    SendIO((struct IORequest *)con->rreq);
                                    InitBitMap(&con->sbm,1,4 * XSIZE,YSIZE);
                                    InitRastPort(&con->srp);
                                    con->srp.BitMap = &con->sbm;
                                    initcursor(con);
                                    if (GetSprite(con->sp,3) != -1) {
                                        SetDrMd(con->rp,JAM2);
                                        con->prt = NULL;
                                        reset(con);
                                        return(0);
                                        }
                                    closefonts(con);
                                    }
                                ClearPointer(con->win);
                                freemem(con);
                                }
                            CloseDevice((struct IORequest *)con->wreq);
                            }
                        CloseWindow(con->win);
                        }
                    CloseScreen(con->scr);
                    }
                DeleteStdIO(con->rreq);
                }
            DeletePort(con->rport);
            }
        DeleteStdIO(con->wreq);
        }
    DeletePort(con->wport);
    }
return(-1);
}

initcursor(register struct console *con)
{
register USHORT *wpt,cnt;

wpt = con->dat;
*wpt++ = 0;
*wpt++ = 0;
con->sbm.Planes[0] = (PLANEPTR)wpt;
for (cnt = 0; cnt < YSIZE * 2; cnt++) *wpt++ = 0x7800;
*wpt++ = 0xFFFF;
*wpt = 0xFF7F;
con->sp->posctldata = con->dat;
return(0);
}

closeconsole(register struct console *con)
{
freeaudio(con);
if (con->prt) CLOSEFILE(con->prt);
FreeSprite(con->sp->num);
closefonts(con);
if (!(con->gstat & GAD)) ClearPointer(con->win);
freemem(con);
CloseDevice((struct IORequest *)con->wreq);
CloseWindow(con->win);
CloseScreen(con->scr);
DeleteStdIO(con->rreq);
DeletePort(con->rport);
DeleteStdIO(con->wreq);
DeletePort(con->wport);
return(0);
}

allocmem(register struct console *con)
{
if ((con->dummy = (UWORD *)AllocMem(DUMMYSIZE,MEMF_CHIP|MEMF_CLEAR))!=NULL) {
    if ((con->dat = (UWORD *)AllocMem(SPRITESIZE,MEMF_CHIP)) != NULL) {
        if ((con->conv = AllocMem(CONVSIZE,0)) != NULL) {
            if ((con->buf = AllocMem(BUFSIZE,0)) != NULL) {
                if ((con->rows = (UBYTE *)AllocMem(rows + 1,0)) != NULL)
                    return(0);
                FreeMem(con->conv,CONVSIZE);
                }
            FreeMem(con->buf,BUFSIZE);
            }
        FreeMem((char *)con->dat,SPRITESIZE);
        }
    FreeMem((char *)con->dummy,DUMMYSIZE);
    }
return(-1);
}

freemem(register struct console *con)
{
FreeMem((char *)con->rows,rows + 1);
FreeMem(con->conv,CONVSIZE);
FreeMem(con->buf,BUFSIZE);
FreeMem((char *)con->dat,SPRITESIZE);
FreeMem((char *)con->dummy,DUMMYSIZE);
return(0);
}

static UBYTE alloc[] = { 15,14,13,11,7,3,5,6,9,10,12,8,4,2,1 };
static UBYTE wave[] = { 0,63,127,63,0,-64,-128,-64 };

beep(struct console *con)
{
register struct IOAudio *audio = con->audio;
register UBYTE          *temp;

if (audio != NULL) {
    audio->ioa_Request.io_Unit = con->channels;
    if (con->sound) {
        if (GetMsg(audio->ioa_Request.io_Message.mn_ReplyPort) == NULL)
            return(-1);
        }
    BeginIO((struct IORequest *)audio);
    if (audio->ioa_Request.io_Error) {
        while (GetMsg(audio->ioa_Request.io_Message.mn_ReplyPort) == NULL);
        temp = audio->ioa_Data;
        audio->ioa_Request.io_Command = ADCMD_ALLOCATE;
        audio->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
        audio->ioa_Data = alloc;
        audio->ioa_Length = sizeof(alloc);
        BeginIO((struct IORequest *)audio);
        audio->ioa_Request.io_Command = CMD_WRITE;
        audio->ioa_Request.io_Flags = ADIOF_PERVOL;
        audio->ioa_Data = temp;
        audio->ioa_Length = sizeof(wave);
        if (audio->ioa_Request.io_Error) con->sound = 0;
        else {
            con->channels = audio->ioa_Request.io_Unit;
            BeginIO((struct IORequest *)audio);
            con->sound = 1;
            }
        }
    else con->sound = 1;
    }
return(0);
}

allocaudio(struct console *con)
{
register struct IOAudio *audio = con->audio;

audio = (struct IOAudio*)AllocMem(sizeof(struct IOAudio),MEMF_CLEAR);
if (audio != NULL) {
    audio->ioa_Request.io_Message.mn_ReplyPort = CreatePort("beep",0);
    if (audio->ioa_Request.io_Message.mn_ReplyPort != NULL) {
        audio->ioa_Request.io_Message.mn_Node.ln_Pri = BEEPPRIORITY;
        audio->ioa_Data = alloc;
        audio->ioa_Length = sizeof(alloc);
        if (!(OpenDevice(AUDIONAME,0,(struct IORequest *)audio,0))) {
            audio->ioa_Data = AllocMem(sizeof(wave),MEMF_CHIP);
            if (audio->ioa_Data != NULL) {
                memcpy(audio->ioa_Data,wave,sizeof(wave));
                audio->ioa_Request.io_Command = CMD_WRITE;
                audio->ioa_Request.io_Flags = ADIOF_PERVOL;
                audio->ioa_Length = sizeof(wave);
                audio->ioa_Period = BEEPPERIOD;
                audio->ioa_Volume = BEEPVOLUME;
                audio->ioa_Cycles = BEEPCYCLES;
                con->audio = audio;
                con->channels = audio->ioa_Request.io_Unit;
                con->sound = 0;
                return(0);
                }
            CloseDevice((struct IORequest *)audio);
            }
        DeletePort(audio->ioa_Request.io_Message.mn_ReplyPort);
        }
    FreeMem((char *)audio,sizeof(struct IOAudio));
    }
con->audio = NULL;
return(-1);
}

freeaudio(struct console *con)
{
register struct IOAudio *audio = con->audio;

if (audio != NULL) {
    audio->ioa_Request.io_Unit = con->channels;
    FreeMem((char *)audio->ioa_Data,sizeof(wave));
    CloseDevice((struct IORequest *)audio);
    DeletePort(audio->ioa_Request.io_Message.mn_ReplyPort);
    FreeMem((char *)audio,sizeof(struct IOAudio));
    }
return(0);
}

readserial(register struct console *con,SHORT linemode)
{
register struct serial      *ser;
register struct IOExtSer    *rreq;
register char               *cp,c;
register LONG               len;

ser = con->ser;
cp = con->buf;
rreq = ser->rreq;
if (ser->requested) {
    if ((GetMsg(ser->rport)) == NULL) return(0);
    if (rreq->IOSer.io_Error) {
        ser->requested = 0;
        return(0);
        }
    c = ser->character;
    if (c == END) {
        rreq->IOSer.io_Flags &= (0xFF - IOF_QUICK);
        rreq->IOSer.io_Error = 0;
        BeginIO((struct IORequest *)rreq);
        if (rreq->IOSer.io_Error) {
            GetMsg(ser->rport);
            ser->requested = 0;
            }
        return(0);
        }
    *cp++ = c;
    len = 1;
    }
else len = 0;
rreq->IOSer.io_Flags |= IOF_QUICK;
rreq->IOSer.io_Error = 0;
ser->requested = 1;
while (len < BUFSIZE) {
    BeginIO((struct IORequest *)rreq);
    if (rreq->IOSer.io_Error) {
        GetMsg(ser->rport);
        ser->requested = 0;
        return(len);
        }
    if (!(rreq->IOSer.io_Flags & IOF_QUICK)) return(len);
    c = ser->character;
    if (c == END) {
        rreq->IOSer.io_Flags &= (0xFF - IOF_QUICK);
        BeginIO((struct IORequest *)rreq);
        if (rreq->IOSer.io_Error) {
            GetMsg(ser->rport);
            ser->requested = 0;
            }
        return(len);
        }
    *cp++ = c;
    len++;
    if (linemode) {
        if ((c == LF)||(c == FF)) {
            rreq->IOSer.io_Flags &= (0xFF - IOF_QUICK);
            BeginIO((struct IORequest *)rreq);
            if (rreq->IOSer.io_Error) {
                GetMsg(ser->rport);
                ser->requested = 0;
                }
            return(len);
            }
        }
    }
return(len);
}

writeserial(register struct console *con,char *cp,LONG len)
{
register struct serial      *ser;
register struct IOExtSer    *wreq;

ser = con->ser;
len = convert(con,cp,len);
wreq = ser->wreq;
if (!(con->gstat & LOCKED)) {
    wreq->IOSer.io_Length = len;
    wreq->IOSer.io_Data = (APTR)con->conv;
    DoIO((struct IORequest *)wreq);
    }
return(0);
}

writeasync(register struct console *con,LONG len)
{
register struct serial      *ser;
register struct IOExtSer    *wreq;
struct IntuiMessage         *mes;
ULONG                       class;
USHORT                      code,clicks = 0;

ser = con->ser;
wreq = ser->wreq;
wreq->IOSer.io_Length = len;
wreq->IOSer.io_Data = (APTR)con->conv;
BeginIO((struct IORequest *)wreq);
while ((GetMsg(ser->wport)) == NULL) {
    readserial(con,0);
    if (mes = (struct IntuiMessage *)GetMsg(con->win->UserPort)) {
        class = mes->Class;
        code = mes->Code;
        ReplyMsg((struct Message *)mes);
        if ((class == MOUSEBUTTONS)&&(code == SELECTDOWN)) {
            clicks++;
            if (clicks == ABORTASYNC) {
                AbortIO((struct IORequest *)wreq);
                GetMsg(ser->wport);
                return(1);
                }
            Delay(ABORTDELAY);
            }
        }
    }
return(0);
}

send_break(register struct serial *ser)
{
register struct IOExtSer    *wreq;
UBYTE                       flags;

wreq = ser->wreq;
flags = wreq->IOSer.io_Flags;
wreq->IOSer.io_Flags &= (0xFF - (IOF_QUICK | SERF_QUEUEDBRK));
wreq->IOSer.io_Command = SDCMD_BREAK;
DoIO((struct IORequest *)wreq);
wreq->IOSer.io_Command = CMD_WRITE;
wreq->IOSer.io_Flags = flags;
return(0);
}

openserial(register struct serial *ser)
{
struct Preferences  prefs;

if ((ser->wport = CreatePort("serial_write",0)) != NULL) {
    if ((ser->wreq = (struct IOExtSer *)CreateExtIO(ser->wport,
                                    sizeof(struct IOExtSer))) != NULL) {
        if ((ser->rport = CreatePort("serial_read",0)) != NULL) {
            if ((ser->rreq = (struct IOExtSer *)CreateExtIO(ser->rport,
                                    sizeof(struct IOExtSer))) != NULL) {
                if (!(OpenDevice("serial.device",1,
                                    (struct IORequest *)ser->wreq,0))) {
                    GetPrefs(&prefs,sizeof(struct Preferences));
                    ser->rreq->IOSer.io_Device = ser->wreq->IOSer.io_Device;
                    ser->rreq->IOSer.io_Unit = ser->wreq->IOSer.io_Unit;
                    if (prefs.SerParShk & 0xf0)
                        ser->rreq->io_SerFlags = SERF_PARTY_ON;
                    else ser->rreq->io_SerFlags = 0;
                    if ((prefs.SerParShk >> 4) == SPARITY_ODD)
                        ser->rreq->io_SerFlags |= SERF_PARTY_ODD;
                    if ((prefs.SerParShk & 0x0f) != SHSHAKE_XON)
                        ser->rreq->io_SerFlags |= SERF_XDISABLED;
                    switch (prefs.BaudRate) {
                        case BAUD_110:      ser->rreq->io_Baud = 110;
                                            break;
                        case BAUD_300:      ser->rreq->io_Baud = 300;
                                            break;
                        case BAUD_1200:     ser->rreq->io_Baud = 1200;
                                            break;
                        case BAUD_2400:     ser->rreq->io_Baud = 2400;
                                            break;
                        case BAUD_4800:     ser->rreq->io_Baud = 4800;
                                            break;
                        case BAUD_9600:     ser->rreq->io_Baud = 9600;
                                            break;
                        case BAUD_19200:    ser->rreq->io_Baud = 19200;
                                            break;
                        case BAUD_MIDI:     ser->rreq->io_Baud = 32768;
                                            break;
                        default:            ser->rreq->io_Baud = 2400;
                                            }
                    ser->rreq->io_ReadLen = 8 - (prefs.SerRWBits >> 4);
                    ser->rreq->io_WriteLen = 8 - (prefs.SerRWBits & 0x0f);
                    ser->rreq->io_StopBits = (prefs.SerStopBuf >> 4) + 1;
                    ser->rreq->io_CtlChar = 0x11130506;
                    switch (prefs.SerStopBuf & 0x0f) {
                        case SBUF_512:      ser->rreq->io_RBufLen = 512;
                                            break;
                        case SBUF_1024:     ser->rreq->io_RBufLen = 1024;
                                            break;
                        case SBUF_2048:     ser->rreq->io_RBufLen = 2048;
                                            break;
                        case SBUF_4096:     ser->rreq->io_RBufLen = 4096;
                                            break;
                        case SBUF_8000:     ser->rreq->io_RBufLen = 8000;
                                            break;
                        case SBUF_16000:    ser->rreq->io_RBufLen = 16000;
                                            break;
                        default:            ser->rreq->io_RBufLen = 1024;
                        }
                    ser->rreq->io_BrkTime = 500000L;
                    ser->rreq->IOSer.io_Command = SDCMD_SETPARAMS;
                    if (!(DoIO((struct IORequest *)ser->rreq))) {
                        ser->rreq->IOSer.io_Command = CMD_READ;
                        ser->rreq->IOSer.io_Data = (APTR)&ser->character;
                        ser->rreq->IOSer.io_Length = 1;
                        ser->rreq->IOSer.io_Error = 0;
                        ser->wreq->IOSer.io_Command = CMD_WRITE;
                        BeginIO((struct IORequest *)ser->rreq);
                        ser->requested = 1;
                        if (ser->rreq->IOSer.io_Error == 0) return(0);
                        }
                    CloseDevice((struct IORequest *)ser->wreq);
                    }
                DeleteExtIO((struct IORequest *)ser->rreq);
                }
            DeletePort(ser->rport);
            }
        DeleteExtIO((struct IORequest *)ser->wreq);
        }
    DeletePort(ser->wport);
    }
return(-1);
}

closeserial(register struct serial *ser)
{
CloseDevice((struct IORequest *)ser->wreq);
DeleteExtIO((struct IORequest *)ser->rreq);
DeletePort(ser->rport);
DeleteExtIO((struct IORequest *)ser->wreq);
DeletePort(ser->wport);
return(0);
}
