/*
 *   Copyright (C) 1988-2006 Lucent Technologies Inc. and others.
 *   All Rights Reserved.
 */

/* common mouse interface functions */

#include "mouse.h"
#include <stdio.h>	/* NULL */
#ifdef UNIXPC		/* build command requires #ifdef instead of #if */
#include <ctype.h>	/* isdigit */
#include <sys/window.h>
#endif

#define ctrl(x)			(x & 037)

typedef	enum	{	/* boolean data type */
	NO,
	YES
} BOOL;

MOUSETYPE mouse;

static	MOUSEMENU *loadedmenu;
static	BOOL	changemenu = YES;
#if UNIXPC
static	int	uw_hs, uw_vs;	/* character height and width */
#endif

/* see if there is a mouse interface */

void
initmouse()
{
	char	*s, *term;
	char	*getenv();

	if ((term = getenv("TERM")) == NULL) {
		return;
	}
	if (strcmp(term, "emacsterm") == 0 || strcmp(term, "viterm") == 0) {
		mouse = EMACSTERM;
	}
	/* the MOUSE enviroment variable is for 5620 terminal programs that
	   have mouse support but the TERM environment variable is the same
	   as a terminal without a mouse, such as myx */
	else if ((s = getenv("MOUSE")) != NULL && strcmp(s, "myx") == 0) {
		mouse = MYX;
	}
#if UNIXPC
	else if (strcmp(term, "s4") == 0 || strcmp(term, "s120") == 0 ||
	    strcmp(term, "s90") == 0) {
		int retval;
		struct uwdata uwd;	/* window data */
		struct umdata umd;	/* mouse data */

		/* get the character size */
		retval = ioctl(1, WIOCGETD, &uwd);
		if (retval || uwd.uw_hs <= 0 || uwd.uw_vs <= 0) {

			/* something wrong with the kernel, so fake it */
			if (!strcmp(term, "s4")) {
				uw_hs = 9;
				uw_vs = 12;
			}
			else {
				uw_hs = 6;
				uw_vs = 10;
			}
		}
		else {	/* kernel is working and knows about this font */
			uw_hs = uwd.uw_hs;
			uw_vs = uwd.uw_vs;
		}
		/* turn on mouse reporting */
		if ((retval = ioctl(1, WIOCGETMOUSE, &umd)) != -1) {
			umd.um_flags = MSDOWN + MSUP;
			ioctl(1, WIOCSETMOUSE, &umd);
		}
		mouse = PC7300;
	}
#endif /* UNIXPC */
	if ((s = getenv("MOUSEMENU")) != NULL && strcmp(s, "none") == 0) {
		changemenu = NO;
	}
	initmenu();
}

/* reinitialize the mouse in case curses changed the attributes */

void
reinitmouse()
{
	if (mouse == EMACSTERM) {

		/* enable the mouse click and sweep coordinate control sequence */
		(void) printf("\033{2");
		if (changemenu) {
			(void) printf("\033#2");	/* switch to menu 2 */
		}
		(void) fflush(stdout);
	}
}

/* restore any original mouse attributes not handled by terminfo */

void
cleanupmouse()
{
	register int	i;

	if (mouse == MYX && loadedmenu != NULL) {
		
		/* remove the mouse menu */
		(void) printf("\033[6;0X\033[9;0X");
		for (i = 0; loadedmenu[i].text != NULL; ++i) {
			(void) printf("\033[0;0x");
		}
		loadedmenu = NULL;
	}
}

/* download a mouse menu */

void
downloadmenu(menu)
MOUSEMENU *menu;
{
	register int	i;
	int	len;

	switch (mouse) {
	case EMACSTERM:
		reinitmouse();
		(void) printf("\033V1");	/* display the scroll bar */
		if (changemenu) {
			(void) printf("\033M0@%s@%s@", menu[0].text, menu[0].value);
			for (i = 1; menu[i].text != NULL; ++i) {
				(void) printf("\033M@%s@%s@", menu[i].text, menu[i].value);
			}
		}
		(void) fflush(stdout);
		break;
	case MYX:
		if (changemenu) {
			cleanupmouse();
			(void) printf("\033[6;1X\033[9;1X");
			for (i = 0; menu[i].text != NULL; ++i) {
				len = strlen(menu[i].text);
				(void) printf("\033[%d;%dx%s%s", len, len + strlen(menu[i].value), 
					menu[i].text, menu[i].value);
			}
			(void) fflush(stdout);
			loadedmenu = menu;
		}
		break;
	}
}

/* draw the scroll bar */

void
drawscrollbar(top, bot, total)
int top, bot, total;
{
	register p1, p2;

	if (mouse == EMACSTERM) {
		if (bot > top && total > 0) {
			p1 = 16 + (top - 1) * 100 / total;
			p2 = 16 + (bot - 1) * 100 / total;
			if (p2 > 116) {
				p2 = 116;
			}
			if (p1 < 16) {
				p1 = 16;
			}
			/* don't send ^S or ^Q to avoid hanging a layer using
			   cu(1) */
			if (p1 == ctrl('Q') || p1 == ctrl('S')) {
				++p1;
			}
			if (p2 == ctrl('Q') || p2 == ctrl('S')) {
				++p2;
			}
		}
		else {
			p1 = p2 = 16;
		}
		(void) printf("\033W%c%c", p1, p2);
	}
}

/* translate a mouse click or sweep to a selection */

mouseselection(p, offset, maxselection)
MOUSEEVENT *p;
int	offset, maxselection;
{
	register int	i;

	i = p->y1 - offset;
	if (i < 0) {
		i = 0;
	}
	else if (i >= maxselection) {
		i = maxselection - 1;
	}
	return(i);
}

/* get the mouse event */

MOUSEEVENT *
getmouseevent()
{
	static	MOUSEEVENT	m;

	if (mouse == EMACSTERM) {
		switch (mygetch()) {
		case ctrl('_'):		/* click */
			if ((m.button = mygetch()) == '0') { /* if scroll bar */
				m.percent = getpercent();
			}
			else {
				m.x1 = getcoordinate();
				m.y1 = getcoordinate();
				m.x2 = m.y2 = -1;
			}
			break;
	
		case ctrl(']'):		/* sweep */
			m.button = mygetch();
			m.x1 = getcoordinate();
			m.y1 = getcoordinate();
			m.x2 = getcoordinate();
			m.y2 = getcoordinate();
			break;
		default:
			return(NULL);
		}
		return(&m);
	}
#if UNIXPC
	/* An ESC character has been received.  See if it is a mouse report,
	 * which looks like: <ESC>[?xx;yy;b;rM
	 */
	if (mouse == PC7300) {
		int	x = 0, y = 0, button = 0, reason = 0, i;
	
		/* Get a mouse report.  The form is: XX;YY;B;RM where
		 * XX is 1, 2, or 3 decimal digits with the X pixel position.
		 * Similarly for YY.  B is a single decimal digit with the
		 * button number (4 for one, 2 for two, and 1 for three).
		 * R is the reason for the mouse report.
		 *
		 * In general, the input is read until the mouse report has
		 * been completely read in or we have discovered that this
		 * escape sequence is NOT a mouse report.  In the latter case
		 * return the last character read to the input stream with
		 * ungetch().
		 */
		
		/* check for "[?" being next 2 chars */
		if ((i = mygetch()) != '[' || (i = mygetch()) != '?') {
			(void) ungetch(i);
			return(NULL);
		}
		/* grab the X position (in pixels) */
		while(isdigit(i = mygetch())) {
			x = (x*10) + (i - '0');
		}
		if (i != ';') {
			(void) ungetch(i);
			return(NULL);	/* not a mouse report */
		}
		/* grab the Y position (in pixels) */
		while(isdigit(i = mygetch())) {
			y = (y*10) + (i - '0');
		}
		if (i != ';') {
			(void) ungetch(i);
			return(NULL);
		}
		/* get which button */
		if ((button = mygetch()) > '4') {
			(void) ungetch(button);
			return(NULL);
		}
		if ((i = mygetch()) != ';') {
			(void) ungetch(i);
			return(NULL);
		}
		/* get the reason for this mouse report */
		if ((reason = mygetch()) > '8') {
			(void) ungetch(reason);
			return(NULL);
		}
		/* sequence should terminate with an 'M' */
		if ((i = mygetch()) != 'M') {
			(void) ungetch(i);
			return(NULL);
		}
		/* We get a mouse report whenever a button is depressed or
		 * released.  Ignore the report whenever the button is
		 * depressed.
		 */
		if (reason != '2') {	/* '2' means button is released */
			return(NULL);
		}
		/* Always indicate button 1 irregardless of which button was
		 * really pushed.
		 */
		m.button = 1;

		/* Convert pixel coordinates to line and column coords.
		 * The height and width are obtained using an ioctl() call
		 * in initmouse().  This assumes that variable width chars
		 * are not being used ('though it would probably work anyway).
		 */
		m.x1 = x / uw_hs;	/* pixel/horizontal_spacing */
		m.y1 = y / uw_vs;	/* pixel/vertical_spacing   */
	
		/* "null" out the other fields */
		m.percent = m.x2 = m.y2 = -1;
		return(&m);
	}
#endif
	return(NULL);
}

/* get a row or column coordinate from a mouse button click or sweep */

getcoordinate()
{
	register int  c, next;

	c = mygetch();
	next = 0;
	if (c == ctrl('A')) {
		next = 95;
		c = mygetch();
	}
	if (c < ' ') {
		return (0);
	}
	return (next + c - ' ');
}

/* get a percentage */

getpercent()
{
	register int c;

	c = mygetch();
	if (c < 16) {
		return(0);
	}
	if (c > 120) {
		return(100);
	}
	return(c - 16);
}

/* update the window label area */

labelarea(s)
char	*s;
{
	static	BOOL	labelon;
	
	switch (mouse) {
	case EMACSTERM:
		if (labelon == NO) {
			labelon = YES;
			(void) printf("\033L1");	/* force it on */
		}
		(void) printf("\033L!%s!", s);
		return(1);
	case MYX:
		(void) printf("\033[?%dv%s", strlen(s), s);
		return(1);
	}
	return(0);	/* no label area */
}
