/*
 * WINDOW.C
 *
 *	DNET interactive terminal window.
 */

#include "dnet.h"

#ifdef __GNUC__
#include "server/servers.h"
#include "dnet/channel.h"
#include "lib/dnetlib.h"
#else
#include "/server/servers.h"
#include "/dnet/channel.h"
#include "/lib/dnetlib.h"
#endif

void setparity();
void addparity();
void OpenConsole();
void CloseConsole();
void LoadConnectList();
void UnLoadConnectList();
void CheckScript(char);
void CloseScript(void);


IOSER *NetReadReady();
IOSER *NetAbortRead();

typedef struct TextAttr TA;
typedef struct IntuiText ITEXT;
typedef struct IntuiMessage IMESS;
typedef struct Screen SCR;
typedef struct Library LIB;
typedef struct Menu  MENU;
typedef struct MenuItem ITEM;

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

TA Ta = { (ubyte *)"topaz", 8 };

extern char * ScriptFile;   /* pointer to script file currently being read */

ITEXT IText[] = {
    { 0, 1, JAM2, 0, 0, &Ta, (ubyte *)"SendBreak"   },
    { 0, 1, JAM2, 0, 0, &Ta, (ubyte *)"StartDNET"   },
    { 0, 1, JAM2, 0, 0, &Ta, (ubyte *)"QUIT"        }
};

ITEM Item[] = {
    { &Item[1], 0, 0, 120, 10, ITEMTEXT|COMMSEQ|ITEMENABLED|HIGHCOMP, 0, (APTR)&IText[0], NULL, 'b' },
    { &Item[2], 0,10, 120, 10, ITEMTEXT|COMMSEQ|ITEMENABLED|HIGHCOMP, 0, (APTR)&IText[1], NULL, 's' },
    { NULL    , 0,20, 120, 10, ITEMTEXT|COMMSEQ|ITEMENABLED|HIGHCOMP, 0, (APTR)&IText[2], NULL, 'q' }
};

MENU Menu[] = {
    { NULL    , 0, 0, 120, 20, MENUENABLED, "DnetControl", &Item[0] }
};

ubyte Title[80];

void
do_dnetwindow(oldbaud)
{
    static struct NewWindow Nw = {
	50, 50, 320, 100, -1, -1,
	CLOSEWINDOW|MENUPICK,
	WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|NOCAREREFRESH|ACTIVATE,
	NULL, NULL, Title, NULL, NULL,
	32, 32, -1, -1, WBENCHSCREEN
    };
    struct Window *win;
    char RcvBuf[128];
    char Cc;
    char RSync = 0;
    long imask, smask, conmask, mask, dnet_mask;
#ifdef NOTDEF
    long ipc_mask;
#endif
    IOCON *iocr, *iocw;
    char notdone = 1;

    sprintf(Title, "DNET V%s%s", VERSION, DNET_VERSION);
    setparity(0xFF, 0x00, 1);
    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);
    if (IntuitionBase == NULL || GfxBase == NULL)
	goto e1;
    AdjustNewWindow( &Nw);
    win = OpenWindow(&Nw);
    if (win == NULL)
	goto e1;
    SetMenuStrip(win, Menu);
    OpenConsole(win, &iocr, &iocw);
    if (iocr == NULL)
	goto e3;
    if (Cto_act) {
	AbortIO((IOR *)&Cto);
	WaitIO((IOR *)&Cto);
	Cto_act = 0;
    }
    if (Wto_act) {
	AbortIO((IOR *)&Wto);
	WaitIO((IOR *)&Wto);
	Wto_act = 0;
    }
    SetBaudRate(oldbaud);

    if (OpenCfgFile()) {    /*  Automatic enviroment reset.     */
	char *env;
	char *get = (AutoAnswer) ? "ENVA" : ((DialOut) ? "ENVO" : "ENVM");

	while (env = GetCfgLine(get)) {
	    char *ptr;
	    for (ptr = env; *ptr && *ptr != ' ' && *ptr != 9; ++ptr);
	    if (*ptr) {
		*ptr = 0;
		for (++ptr; *ptr == ' ' || *ptr == 9; ++ptr);
		if(DDebug) printf("Setting env: %s=%s\n", env, ptr);
		SetDEnv(env, ptr);
	    }
	}
    }

    if (Cd == 0 && AutoAnswer) {
	if (OpenCfgFile()) {
	    char *str;
	    while (str = GetCfgLine("RESM")) {  /* RESM MODEM-CMD   */
		short len = strlen(str);
		str[len++] = '\r';
		NetWrite(str, len);
		Cto.tr_time.tv_secs = 1;
		Cto.tr_time.tv_micro= 0;
		DoIO((IOR *)&Cto);
	    }
	    CloseCfgFile();
	} else {
	    NetWrite("AT\r", 3);   /*  cause modem to reset baud rate */
	    Cto.tr_time.tv_secs = 1;
	    Cto.tr_time.tv_micro= 0;
	    DoIO((IOR *)&Cto);
	    NetWrite("AT\r", 3);     /*  cause modem to reset baud rate */
	    Cto.tr_time.tv_secs = 1;
	    Cto.tr_time.tv_micro= 0;
	    DoIO((IOR *)&Cto);
	    NetWrite("ATS0=1\r", 7); /*  auto answer on */
	}
    }

    iocr->io_Data = (APTR)&Cc;
    iocr->io_Length = 1;
    SendIO((IOR *)iocr);

    imask   = 1 << win->UserPort->mp_SigBit;
    smask   = 1 << IOSink->mp_SigBit;
    conmask = 1 << iocr->io_Message.mn_ReplyPort->mp_SigBit;
    dnet_mask = 1 << DNetPort->mp_SigBit;
#ifdef NOTDEF
    ipc_mask= 1 << IPCPort->mp_SigBit;
#endif

    LoadConnectList();

    Signal(FindTask(NULL), imask|smask|conmask|dnet_mask/*|ipc_mask*/);

    StartWriteTimeout(1);
    if(DDebug) printf("StartWriteTimeout(1)\n");

    if(ScriptFile != NULL) CheckScript('\n');  /* init autoscript */

    /* while haven't quit, and aren't online */
    while (!Quit && OnLine == 0) {
	mask = Wait(imask|smask|conmask|dnet_mask/*|ipc_mask*/);
#ifdef NOTDEF
	if (mask & ipc_mask)
	    handle_ipc();
#endif
	/* handle DNet port io. Since we're not connected yet, 
	** all io is error, and ports should be closed. 
	** new openings should block/delay until we are connected */
	if (mask & dnet_mask) {  
	    IOSTD *ior;
	    while (ior = (IOSTD *)GetMsg(DNetPort)) {
		switch(ior->io_Command) {
		default:
		case DNCMD_WRITE:
		case DNCMD_EOF:
		case DNCMD_IOCTL:
		case DNCMD_QUIT:
		case DNCMD_OPEN:
		    ior->io_Error = 1;
		    break;
		case DNCMD_CLOSE:
		    Chan[(ulong)ior->io_Unit].state = CHAN_FREE;
		    break;
		case DNCMD_SOPEN:
		    {
			uword chan = (ulong)ior->io_Unit;
			Chan[chan].state = CHAN_FREE;
			if (!ior->io_Error) {
			    Chan[chan].state = CHAN_CLOSE;
			    Chan[chan].port  = (PORT *)ior->io_Offset;
			    Chan[chan].flags = CHANF_RCLOSE;
			    WritePort(Chan[chan].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, chan);
			}
			if (ior->io_Length)
			    FreeMem(ior->io_Data, ior->io_Length);
			FreeMem(ior, sizeof(IOSTD));
			ior = NULL;
		    }
		    break;
		case DNCMD_EXEC:
		    Execute((char *)ior->io_Offset, NULL, NULL);
		    break;
		}
		if (ior)
		    ReplyMsg((MSG *)ior);
	    }
	}

	/* intuition io */
	if (mask & imask) {
	    struct IntuiMessage *im;
	    while (im = (IMESS *)GetMsg(win->UserPort)) {
		switch(im->Class) {
		case CLOSEWINDOW:
		    notdone = 0;
		    Quit = 1;
		    break;
		case MENUPICK:
		    switch((uword)((MENUNUM(im->Code)<<8)|ITEMNUM(im->Code))) {
		    case 0x0000:    /*	menu 0 item 0	*/
			NetBreak();
			break;
		    case 0x0001:    /*	menu 0 item 1	*/
			notdone = 0;
			OnLine = 1;
			break;
		    case 0x0002:    /*	menu 0 item 2	*/
			notdone = 0;
			Quit = 1;
			break;
		    case 0x0100:    /*	menu 1 item 0	*/
			break;
		    }
		    break;
		}
		ReplyMsg((MSG *)im);
	    }
	}

	/* serial io mask */
	if (mask & smask) {
	    IOSER *ios;
	    if(DDebug > 4 ) printf("Serial read ready:\n"); 
	    if (ios = NetReadReady()) {
		int n;
		int actual;
		ubyte *ptr;

		WaitIO((IOR *)ios);
		actual = NetReadReturned(&ptr);
		if (actual > 0) {
		    for (n = 0; n < actual; ++n)
			ptr[n] &= 0x7F;  /* strip to 7 bits */
		    if (RSync && *ptr == PKCMD_RESTART) {
			OnLine = 1;
			notdone = 0;
		    } else if( *ptr == SYNC){
		    	RSync = 1;
		    } else {
		        RSync = 0;
			if(ScriptFile != NULL) 
			    for(n=0;n<actual;++n)CheckScript(ptr[n]);
		    }
		    oldbaud = CheckConnect(oldbaud, ptr, n);
		    iocw->io_Data   = (APTR)ptr;
		    iocw->io_Length = n;
		    DoIO((IOR *)iocw);
		}
		if ((n = NetReady()) >= sizeof(RcvBuf))
		    n = sizeof(RcvBuf) - 1;
		if (n <= 0)
		    n = 1;
		NetStartRead(n);
	    }
	    if (CheckIO( (IOR *) &Wto)) {
		StartWriteTimeout(1);
		NetReady();
	    }
	}
	if (mask & conmask) {
	    if (CheckIO((IOR *)iocr)) {
		WaitIO((IOR *)iocr);
		if (!Master8)
		    addparity((ubyte *)iocr->io_Data, iocr->io_Actual);
		NetWrite(iocr->io_Data, iocr->io_Actual);
		NetWaitWrite();
		iocr->io_Data = (APTR)&Cc;
		iocr->io_Length = 1;
		SendIO((IOR *)iocr);
	    }
	}
	NetReady();
	if (Getty && !Cd) {
	    Quit = 1;
	    notdone = 0;
	    break;
	}
	if (AutoAnswer && Cd)
	    break;
    }

    StartWriteTimeout(0);

    UnLoadConnectList();

    {
	IOSER *ios = NetAbortRead();

	if (ios->IOSer.io_Actual > 0) {
	    ubyte *ptr = (ubyte *)ios->IOSer.io_Data;
	    short n;
	    for (n = 0; n < ios->IOSer.io_Actual; ++n)
		ptr[n] &= 0x7F;
	    oldbaud = CheckConnect(oldbaud, ptr, n);
	}
    }

    if (DDebug)
	printf("SETTING BAUD RATE TO %d\n", oldbaud);

    if (AutoAnswer && Cd) {
	char *msg = "\r\nDNET AutoAnswer, Protocol Start\r\n";
	OnLine = 1;
	NetWrite(msg, strlen(msg));
	Cto.tr_time.tv_secs = 3;
	Cto.tr_time.tv_micro= 0;
	DoIO((IOR *)&Cto);
    }

    NetStartRead(3);

    AbortIO((IOR *)iocr);
    WaitIO((IOR *)iocr);
    CloseScript();
    CloseConsole(iocr, iocw);
e3:
    CloseWindow(win);
e1:
    if (IntuitionBase)  CloseLibrary((LIB *)IntuitionBase);
    if (GfxBase)        CloseLibrary((LIB *)GfxBase);
    if (Quit)
	puts("DNET EXITING");
    else
	puts("DNET RUNNING");
    return;
}

/*
 *  PARITY ROUTINES
 */

static ubyte Party[256/8];

void
setparity(pand, por, peven)
int pand, por, peven;
{
    short i, j, k, l;

    BZero(Party, sizeof(Party));
    for (i = 0; i < 128; ++i) {
	for (j = (~i & 127), k = 0; j; j >>= 1) {   /*  k = count # of 0's */
	    if (j & 1)
		++k;
	}
	k ^= peven;
	l = (((k & 1) ? 0x01 : 0) | por) & pand;
	Party[i >> 3] |= l << (i & 7);
    }
}

void
addparity(buf, bytes)
ubyte *buf;
int bytes;
{
    short i;

    for (i = bytes - 1; i >= 0; --i) {
	if (Party[buf[i]>>3] & (1 << (buf[i]&7)))
	    buf[i] |= 0x80;
    }
}

void
OpenConsole(win, piocr, piocw)
IOCON **piocr, **piocw;
struct Window *win;
{
    PORT *port;
    static IOCON iocr, iocw;
    int error;

    port = CreatePort(NULL, 0);
    iocr.io_Command = CMD_READ;
    iocr.io_Data = (APTR)win;
    iocr.io_Message.mn_Node.ln_Type = NT_MESSAGE;
    iocr.io_Message.mn_ReplyPort = port;
    error = OpenDevice("console.device", 0, (IOR *)&iocr, 0);
    if (!error) {
	iocw = iocr;
	iocw.io_Command = CMD_WRITE;
	*piocr = &iocr;
	*piocw = &iocw;
    } else {
	*piocr = *piocw = NULL;
    }
}

void
CloseConsole(iocr, iocw)
IOCON *iocr;
IOCON *iocw;
{
    CloseDevice((IOR *)iocr);
    DeletePort(iocr->io_Message.mn_ReplyPort);
}


/* we have a NewWindow structure that might have negative values in its
** size or position fields
** we interpret these negative values to mean relative to relevant screen
** size.
** i.e. negative LeftEdge is that distance from screen's right edge
** it works sort of like geometry strings in X
*/
AdjustNewWindow( struct NewWindow * NewW){
    int screenwidth = 320;
    int screenheight = 200;
    struct Screen *ScreenInfo;

    /* grab the default screen and hold it until we get info on it... */
    if((ScreenInfo =LockPubScreen( NULL )) != NULL){
	screenwidth = ScreenInfo->Width;
	screenheight = ScreenInfo->Height;
        UnlockPubScreen(NULL,ScreenInfo);
    } else {
        printf("Hrm, can\'t get information on default screen!\n");
    }

    if(NewW->Height < 0){
    	NewW->Height = screenheight + NewW->Height;
	if(NewW->Height < 0)  /* someone specified a way negative size? */
		NewW->Height = screenheight;
    } else if(NewW->Height == 0){
        NewW->Height = screenheight / 5;   /* a sane default size? */
    } else { /* newW->Height > 0 */
        if(NewW->Height > screenheight ){  /* you really want a big window?? */
	    NewW->Height = screenheight;
	}
    }
    if(NewW->Width < 0){
    	NewW->Width = screenwidth + NewW->Width;
	if(NewW->Width < 0)  /* someone specified a way negative size? */
		NewW->Width = screenwidth;
    } else if(NewW->Width == 0){
        NewW->Width = screenwidth / 5;   /* a sane default size? */
    } else { /* newW->Width > 0 */
        if(NewW->Width > screenwidth ){  /* you really want a big window?? */
	    NewW->Width = screenwidth;
	}
    }

    /* handle negative offsets for window positions */
    if(NewW->LeftEdge < 0){
    	NewW->LeftEdge = screenwidth + NewW->LeftEdge;
    } 
    if(NewW->TopEdge < 0){
    	NewW->TopEdge = screenheight + NewW->TopEdge;
    } 

    if(NewW->LeftEdge + NewW->Width > screenwidth){
    	NewW->LeftEdge = screenwidth - NewW->Width;
    }
    if(NewW->TopEdge + NewW->Height > screenheight){
    	NewW->TopEdge = screenheight - NewW->Height;
    }

}

/*
 *
 */

static MLIST CList;

void
LoadConnectList()
{
    char *ptr;

    NewList((LIST *)&CList);

    OpenCfgFile();
    while (ptr = GetCfgLine("AUTA")) {
	long baudrate = atoi(ptr);
	while (*ptr && *ptr != '\"')
	    ++ptr;
	if (*ptr == '\"') {
	    char *en;
	    NODE *node;

	    for (en = ++ptr; *en && *en != '\"'; ++en);
	    *en = 0;

	    if (node = (NODE *)malloc(sizeof(NODE)+strlen(ptr)+1)) {
		AddTail((LIST *)&CList, node);
		node->ln_Name = (char *)baudrate;
		strcpy((char *)(node + 1), ptr);
	    }
	}
    }
    CloseCfgFile();
}

void
UnLoadConnectList()
{
    NODE *node;
    while (node = RemHead((LIST *)&CList))
	free(node);
}

int
CheckConnect(oldbaud, ptr, n)
int oldbaud;
char *ptr;
int n;
{
    static char tmpbuf[64];
    static short tlen;
    short i;
    NODE *node;
    long newbaud = oldbaud;

    for (i = 0; i < n; ++i) {
	if (ptr[i] == 13 || ptr[i] == 10) {
	    tmpbuf[tlen++] = 0;
	    if (tlen < 2) {
		tlen = 0;
		continue;
	    }
	    tlen = 0;
	    for (node = (NODE *)GetHead(&CList); node; node = (NODE *)GetSucc(node)) {
		if (strcmp(tmpbuf, (char *)(node + 1)) == 0) {
		    newbaud = (long)node->ln_Name;
		    if (DDebug>2)
			printf("Successful compare, baud -> %d\n", newbaud);
		    break;
		}
	    }
	    if (GetHead(&CList) == NULL) {
		if (strncmp(tmpbuf, "CONNECT", 7) == 0) {
		    newbaud = atoi(tmpbuf + 7);
		    if (newbaud == 0)
			newbaud = 300;
		}
	    }
	    continue;
	}
	tmpbuf[tlen++] = ptr[i];
	if (tlen == sizeof(tmpbuf))
	    tlen = 0;
    }
    if (oldbaud != newbaud)
	SetBaudRate(newbaud);
    return(newbaud);
}


static char *expectstring = NULL; /* space holding expect string */
static char * expectptr = NULL;   /* ptr to char in expect string looked for */
static FILE * script_file_handle = NULL;  /* open script file */
static char * cmdstring = NULL;
static int cmdstringlen = 80;
/* AutoScript stuff */
void CheckScript(char rcvdchar){
    char * temptr;


    /* Handle any pending expects */
    if(expectptr != NULL){
	if(DDebug >3)printf("Expected %c got %c", *expectptr, rcvdchar);

        if(*expectptr == rcvdchar){
	    expectptr++;
	    if(*expectptr != '\0'){
		if(DDebug >3)printf(" expect %c next", *expectptr);
	    	return;   /* continue expecting */
	    }  
	    /* reached end of expect string! */
	    /* continue on in this function */
	    if(DDebug)
	    	printf("Reached end of expect for %s", expectstring);
	} else {
	    expectptr = expectstring; /* back to start */
	    if(DDebug >3)puts(" expect reset");
	    return;   /* continue expecting */
	}
    }

    if(script_file_handle == NULL && ScriptFile != NULL ){
	if(DDebug) printf("attempting to open %s\n", ScriptFile);
        script_file_handle = fopen(ScriptFile, "r");
	if(script_file_handle == NULL){
	    /* couldn't open script file! */
	    printf("Couldn't open script file %s\n", ScriptFile);
	    ScriptFile = NULL;
	    return;
	}
    }

    /* get next command from the script file until can no longer process
    ** them.  i.e. expect or eof */
    expectptr = NULL;
    while(expectptr == NULL && script_file_handle != NULL){
        if(cmdstring == NULL){
	    cmdstring = (char *) malloc(cmdstringlen);
	    if(cmdstring == NULL){
	    	printf("script memory error!\n");
		CloseScript();
		return;
	    }
	    *cmdstring = '\0';
	}
	temptr = fgets( cmdstring, cmdstringlen, script_file_handle);
	if(temptr == NULL){ 	/* reached EOF of ScriptFILE */
	    if(DDebug)printf("Script EOF reached!\n");
	    CloseScript();
	    break;
	}
	/* deal with commands */
	if(*cmdstring == '\n' || *cmdstring == '#'){
	    if(DDebug > 3)printf("Script comment: %s", cmdstring);
	} else
	if(strncmp(cmdstring, "expect ", 7) == 0){
	    if(expectstring != NULL) free(expectstring);
	    expectstring = strdup(cmdstring+7);
	    if(expectstring == NULL){
	    	printf("script memory error!\n");
		CloseScript();
		return;
	    }

	    StripEndLF(expectstring);
	    if(DDebug)
	    	printf("expecting \"%s\"\n", expectstring);
	    expectptr = expectstring;
	} else 
	if(strncmp(cmdstring, "send ", 5) == 0){
	    temptr = cmdstring + 5;
	    LFTranslate(temptr);  /* translate linefeeds etc */
	    if(DDebug)
	    	printf("sending \"%s\"\n", temptr);
	    NetWrite(temptr, strlen(temptr));
	    /*  Ideally, we should wait for the IORequest NetWrite() makes
	    **  to complete, but this silly code ain't written well enough
	    **  to do that.   Clearly, yet another reason for a major
	    **  rewrite...
	    */
	    usleep(100000); /* Things go bad if its not complete in 1/10 sec*/
	}  else
	if(strncmp(cmdstring, "source ", 7) ==0){
	    temptr = cmdstring+7;
	    StripEndLF(temptr);
	    if(DDebug)
	    	printf("sourcing \"%s\"\n", temptr);
	    if(script_file_handle != NULL)
	        fclose(script_file_handle);
	    script_file_handle = fopen(temptr, "r");
	    if(script_file_handle != NULL){
		ScriptFile = temptr;
		if(DDebug>2)
		    printf("Open succeeded\n");
	    }else{
		if(DDebug>2) printf("Open FAILED\n");
	        CloseScript();
		return;
	    }
	} else {
	    printf("Script unknown: %s", cmdstring);
	}
    }
    return;
}

void CloseScript(){
    if(DDebug)printf("Closing Script!\n");
    if(script_file_handle != NULL){
	if(DDebug>2)printf("Closing file handle !\n");
    	fclose(script_file_handle);
	script_file_handle = NULL;
    }
    if(expectstring != NULL){
	if(DDebug>2)printf("freeing expectstring!\n");
	free(expectstring);
    	expectstring = NULL;
	expectptr = NULL;
    }

    if(cmdstring != NULL){
	if(DDebug>2)printf("freeing cmdstring!\n");
	free(cmdstring);
	cmdstring = NULL;
    }
    ScriptFile = NULL;
    if(DDebug>2)printf("ScriptFile cleared!\n");
}

/* turn linefeed on end of string into a null */
StripEndLF(char * string){
    char * ptr;

    if( string ==  NULL) return;

    if((ptr = rindex(string, '\n')) != NULL) *ptr = '\0';
}

/* translate the 'newlines' from the raw file fgets into characters
** that will actually cause the modem to do stuff... */
LFTranslate(char * string){
    char * ptr;

    if( string ==  NULL) return;

    if((ptr = rindex(string, '\n')) != NULL){
	 if(DDebug>3)printf("translating %d to %d\n", '\n', 13);
    	 *ptr = 13;
    }
}
