/*
 * Copyright 1989 by Georgia Tech Research Corporation, Atlanta, GA.
 * Copyright 1988, 1989 by Robert Viduya.
 * Copyright 1990 Jeff Sparkes.
 *
 *                         All Rights Reserved
 *
 *   Updated 03/1993 George Haddad, Michigan State U. (georgeh@ibm.cl.msu.edu):
 *              Add support for title resource
 *              Add support for cursor resource
 *              Add support for dynamic icon selection
 *              Add support for optional stdin input
 *              Add support for kybd click (if avail) when locked
 *
 *   Updated 04/1993 George Haddad, Michigan State U. (georgeh@ibm.cl.msu.edu):
 *              Apply patchlevel 01
 *              (thanks to Dennis Boone,R.K.Lloyd, Jonathon Abbey)
 */

/*
 *	x3270.c
 */

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Core.h>
#include <X11/Shell.h>
#include "X.h"
#include "version.h"
#include <stdio.h>
#include <signal.h>

Display		*display;
Widget		toplevel;
XtAppContext	appcontext;
Atom		protocol, delete_me;
int		net_sock;
int		background, foreground;
Font		ibmfont;
XFontStruct     *ibmfontinfo;
Bool		kybdlock=FALSE;
int		ROWS, COLS;
int		model_num;
int		retcode = 1;		/* set to 0 by net_input */
int		session_active = TRUE;	/* terminate if FALSE */
XKeyboardControl new_kybd_ctl;          /* new kybd ctl settings */
unsigned long   kybd_ctl_mask=KBKeyClickPercent; /* Bit mask for setting kybd-ctl */

extern XtActionsRec actions[];
extern void	GetFileInput();
extern void	HandleFocusChange();
extern void	HandleInterrupt();
extern int	actioncount;

static XrmOptionDescRec options[]= {
	{ "-charset",	".charset",	XrmoptionSepArg,	"default"},
	{ "-cursor",	".cursor",	XrmoptionSepArg,	"block"},
	{ "-icon",	".icon",	XrmoptionSepArg,	 0 },
	{ "-iconX",	".iconX",	XrmoptionSepArg,	 0 },
	{ "-iconY",	".iconY",	XrmoptionSepArg,	 0 },
	{ "-ititle",	".iconName",	XrmoptionSepArg,	 "" },
	{ "-keymap",	".keymap",	XrmoptionSepArg,	"ncd" },
	{ "-model",	".model",	XrmoptionSepArg,	"4" },
	{ "-mono",	".mono",	XrmoptionNoArg,		"true" },
	{ "-port",	".port",	XrmoptionSepArg,	"telnet" },
	{ "-selection",	".select",	XrmoptionSepArg,	"PRIMARY"},
	{ "-stdin",	".stdinput",	XrmoptionNoArg,		"on" },
};

#define offset(field) XtOffset(AppResptr, field)
static XtResource resources[] = {
	{ "boldColor", "BoldColor", XtRPixel, sizeof(Pixel),
		  offset(bold), XtRString, "cyan", },
	{ "charset",  "Charset",  XtRString, sizeof(char *),
	  	offset(charset), XtRString, "default", },
	{ "colorBackground", "ColorBackground", XtRPixel, sizeof(Pixel),
	  	offset(colorbg), XtRString, "black", },
	{ "cursor",  "Cursor",  XtRString, sizeof(char *),
	  	offset(cursor), XtRString, "block", },
	{ "icon",  "Icon",  XtRString, sizeof(char *),
	  	offset(icon), XtRString, 0, },
	{ "inputColor", "InputColor", XtRPixel, sizeof(Pixel),
	  	offset(select), XtRString, "white", },
	{ "keymap", "Keymap", XtRString, sizeof(char *),
	  	offset(keymap), XtRString, 0, },
	{ "model", "Model", XtRString, sizeof(char *),
	  	offset(model), XtRString, "4", },
	{ "mono", "Mono", XtRBoolean, sizeof(Boolean),
	  	offset(mono), XtRString, "false" },
	{ "normalColor", "NormalColor", XtRPixel, sizeof(Pixel),
	  	offset(normal), XtRString, "green", },
	{ "port", "Port", XtRString, sizeof(char *),
	  	offset(portname), XtRString, "telnet", },
	{ "selection", "Selection", XtRString, sizeof(char *),
	  	offset(selection), XtRString, "PRIMARY", },
	{ "stdinput", "Stdinput", XtRString, sizeof(char *),
	  	offset(stdinput), XtRString, "off", },
	{ XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
	  	offset(background), XtRString, "XtDefaultBackground", },
	{ XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
	  	offset(fontstruct), XtRString, "3270", },
	{ XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
	  	offset(foreground), XtRString, "XtDefaultForeground", },
	{ XtNiconName, XtCIconName, XtRString, sizeof(char *),
	  	offset(iconName), XtRString, "", },
	{ XtNiconX, XtCIconX, XtRInt, sizeof(int),
	  	offset(iconX), XtRInt, 0, },
	{ XtNiconY, XtCIconY, XtRInt, sizeof(int),
	  	offset(iconY), XtRInt, 0, },
	{ XtNtitle, XtCTitle, XtRString, sizeof(char *),
	  	offset(title), XtRString, 0, },
};
#undef offset

extern void net_input();
char *get_resource();
AppRes	appres;
static char *trans = "<Expose>:Redraw()\n";
extern Atom the_selection;


main (argc, argv)
int	argc;
char	*argv[];
{
    Arg		args[12];
    int		i = 0;
    XrmDatabase rdb;
    char	buf[100];
    char	*kbdtrans = 0, *usertrans = 0;
    XtTranslations table;
    char **	fp;
    char **     begin;
    char **	fpn;
    char **     beginn;
    char * 	t;
    char *      tmp;
    char * path="/usr/athena/bin";
    int         num,z,match=0;
    

    XtSetArg(args[i], XtNinput, True); i++;
    XtSetArg(args[i], XtNallowShellResize, False); i++;

    toplevel = XtAppInitialize(&appcontext, "X3270", options, 
	XtNumber(options), &argc, argv, 0, args, i);

    display = XtDisplay(toplevel);

    tmp=(char *)calloc((strlen(path)+1),sizeof(char));
    strcpy(tmp,path);
    fp = XGetFontPath(display,&num);
    begin = fp;
    while ((z<num) && ((match=strcmp(*fp,path))!=0)) {
	z++;*fp++;
    }	
    if (match != 0) {
	fpn=(char **)calloc(num+1,sizeof(char **));
	if (fpn == NULL)
		printf("request failed\n");
	beginn = fpn;
	fp = begin;
	for (z=0;z<num;z++) {
		t = (char *)calloc((strlen(*fp)+1),sizeof(char));
		strcpy(t,*fp);
       		*fpn=t;
		*(fp++);
		*(fpn++);
    	}
	num++;
    
	*(fpn)=tmp;
	fpn = beginn;
	XSetFontPath(display,fpn,num);
    }
    XtGetApplicationResources(toplevel, &appres, resources,
    		XtNumber(resources), 0, 0);

    XtAppAddActions(appcontext, actions, actioncount);
    if (argc < 2) {
	sprintf (buf, "usage: %s [-cursor block/line][-iccn path][-ititle iconName][-iconX x][-iconY y][-keymap name][-model[2345]][-mono][-port portname][-stdin][-selection colour] ibm-host\n",
		argv[0]);
	XtError(buf);
	exit (retcode);
    }
    foreground = appres.foreground;
    background = appres.background;
    ibmfontinfo = appres.fontstruct;
    ibmfont = appres.fontstruct->fid;
    switch (atoi(appres.model)) {
	case 2:
	    ROWS = 24; COLS = 80;
	    model_num = 2;
	    break;
	case 3:
	    ROWS = 32; COLS = 80;
	    model_num = 3;
	    break;
	case 4:
	    ROWS = 43; COLS = 80;
	    model_num = 4;
	    break;
	case 5:
	    ROWS = 27; COLS = 132;
	    model_num = 5;
	    break;
	default:
	    {
		sprintf(buf, "unknown model %s, defaulting to 4", appres.model);
	        XtWarning(buf);
	        ROWS = 43; COLS = 80;
	        model_num = 4;
	    }
	    break;
    }
    net_sock = connect_net (argv[1], appres.portname);
    if (net_sock < 0)
       exit (retcode);
    XtAppAddInput(appcontext, net_sock, XtInputReadMask, net_input, net_sock);
    XtAppAddInput(appcontext, net_sock, XtInputExceptMask, net_input, net_sock);
    the_selection = XInternAtom(display, appres.selection, False);

    if (appres.keymap == NULL)
	appres.keymap = (char *)getenv("KEYMAP");
    if (appres.keymap == NULL)
	appres.keymap = (char *)getenv("KEYBD");
    if (appres.keymap == NULL) {
        XtWarning("Using default keymap");
    } else {
        rdb = XtDatabase(display);
	sprintf(buf, "keymap.%s", appres.keymap);
	kbdtrans = get_resource(rdb, argv[0], buf);
	sprintf(buf, "keymap.%s.user", appres.keymap);
	usertrans = get_resource(rdb, argv[0], buf);
    }

    /* --------------------------------------- */
    /* Use title resource if there - GJH 02/93 */
    /* --------------------------------------- */
    if (appres.title == NULL) {
	sprintf(buf, "%s - %s", argv[0], argv[1]);
        XtSetArg(args[0], XtNtitle, buf);
        XtSetValues(toplevel,args, 1);
    }

    screen_init (kbdtrans, usertrans);
    protocol = XInternAtom(display, "WM_PROTOCOLS", False);
    delete_me = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, XtWindow(toplevel), &delete_me, 1);

    table = XtParseTranslationTable(trans);
    XtOverrideTranslations(toplevel, table);

    if (strcmp(appres.charset, "default")) {
        setup_charset(appres.charset);
    }

    /* --------------------------------------- */
    /* Enable our interrupt and event handlers*/
    /*         ... GJH 03/93                   */
    /* --------------------------------------- */
    if (signal(SIGINT, SIG_IGN) != SIG_IGN )
	signal(SIGINT, HandleInterrupt);
    signal(SIGTERM, HandleInterrupt);

    XtAddEventHandler	(toplevel,
			FocusChangeMask,
			FALSE,
			HandleFocusChange,
			(Opaque)NULL);
    /* --------------------------------------- */
    /* Enable stdin if requested - GJH 03/93   */
    /* --------------------------------------- */
    if (strcmp(appres.stdinput, "on") == 0 )
        XtAppAddInput(appcontext, fileno(stdin), XtInputReadMask, GetFileInput, NULL);
    while (session_active) {
	XtInputMask	im;
	XEvent		event;

	while ((im = XtAppPending(appcontext)) == XtIMXEvent) {
	    XtAppNextEvent(appcontext, &event);
	    /*
	    if (event.type = ClientMessage) {
		XClientMessageEvent *e = (XClientMessageEvent *)&event;

		if (e->message_type == protocol && e->data.l[0] == delete_me)
		    exit(0);
	    } 
	    */

	    XtDispatchEvent(&event);
	}
	screen_disp();
	XtAppProcessEvent(appcontext, XtIMAll);
    }

    /*
    (void) shutdown (net_sock, 2);
    (void) close (net_sock);
    */

/*-----------------------------------------------------*/
/* Destroy the application context,,                                 */
/* Restore keyclick to default if we lose Focus        */
/*                           ... GJH 02/93             */
/*-----------------------------------------------------*/
    if (kybdlock) {
	new_kybd_ctl.key_click_percent = -1;
	XChangeKeyboardControl(display, kybd_ctl_mask, &new_kybd_ctl);
    }
    XtDestroyApplicationContext(appcontext);

    exit (retcode);
}

/*
 * A way to work around bugs with Xt resources.  It seems to be impossible
 * to get arbitrarily named resources.  Someday this should be hacked to
 * add classes too.
 */
char *
get_resource(rdb, prog, name)
    XrmDatabase rdb;
    char	*prog, *name;
{
    XrmValue	value;
    char	*type[20];
    char	buf[20], str[1024];
    char	*s;
    extern char *rindex();

    if ((s = rindex(prog, '/')) != 0)
	prog = s+1;
    sprintf(str, "%s.%s", prog, name);
    if (XrmGetResource(rdb, str, 0, type, &value) == True) {
	return XtNewString(value.addr);
    } else {
	return 0;
    }
}

/* ----------------------------------- */
/* Handle Interrupts, reset kybd click */
/* if needed  ... GJH 03/93            */
/* ----------------------------------- */
void
HandleInterrupt()
{
 
   if (kybdlock) {
	new_kybd_ctl.key_click_percent = -1;
	XChangeKeyboardControl(display, kybd_ctl_mask, &new_kybd_ctl);
    	XtDestroyApplicationContext(appcontext);
   }
   exit(1);
}

/*-----------------------------------------------------*/
/* Handel changes in kybd focus                        */ 
/* Set keyclick ON if we gain focus and kybd is locked */
/* Restore keyclick to default if we lose Focus        */
/*                           ... GJH 02/93             */
/*-----------------------------------------------------*/
 
/*ARGUSED*/
void
HandleFocusChange(w, unused, event)
Widget w;
register        XFocusChangeEvent  *event;
caddr_t         unused;
{
        if (event->type == FocusIn) {
           if (kybdlock)  {
                new_kybd_ctl.key_click_percent = 100;
                XChangeKeyboardControl(display, kybd_ctl_mask, &new_kybd_ctl);
           }
        }   
        else if (event->type == FocusOut) {
           new_kybd_ctl.key_click_percent = -1;
	   XChangeKeyboardControl(display, kybd_ctl_mask, &new_kybd_ctl);
        }
}

