/* di_input.c */

#ifdef  X11_DISP

#include <stdio.h>
#include <math.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include "corewar.h"
#include "cwdisp.h"

#define  MIN_SLOW  1.0    /* The min & max speeds, in cycles/second. */
#define  MAX_SLOW  500.0

static int  last_cursor = -1, last_win = -1;
#define  LC_INBUT  1
#define  LC_NOBUT  0

static Cursor  inbut_cursor, nobut_cursor;
static void  scrollpress(int winnum, int y);


void  di_in_init()  {
	inbut_cursor = XCreateFontCursor(di_dpy, XC_arrow);
	nobut_cursor = XCreateFontCursor(di_dpy, XC_left_ptr);
}


void  di_butpress(Display *dpy, Window win, XButtonPressedEvent *evt)  {
	gbut_mpress(evt->window, evt->x, evt->y);
}


void  di_butrelease(Display *dpy, Window win, XButtonReleasedEvent *evt)  {
	int  butnum;

	butnum = gbut_mrelease(evt->window, evt->x, evt->y);
	if (butnum == GBUT_NOBUT)  {
		XBell(dpy, 0);
		return;
	}
	switch(butnum)  {
	case DI_BSTOP:
		if (di_state == DS_STOPPED)
			di_changestate(dpy, win, DS_STEP);
		else
			di_changestate(dpy, win, DS_STOPPED);
		break;
	case DI_BSLOW:
		if (evt->x == di_but[DI_BSLOW].x)
			++evt->x;
		if (evt->x == di_but[DI_BSLOW].x + di_but[DI_BSLOW].w - 1)
			--evt->x;
		di_slopos = evt->x - di_but[DI_BSLOW].x - 1;
		/* A logarithmic scale. */
		di_speed = (float)exp((double)di_slopos * log(MAX_SLOW) /
													(double)(di_but[DI_BSLOW].w - 2));
		di_changestate(dpy, win, DS_RUNSLOW);
		di_set_timer(di_speed);
		break;
	case DI_BGO:
		di_changestate(dpy, win, DS_RUNFAST);
		break;
	case DI_BQUIT:
		di_exit(0);
		break;
	case DI_BRESTART:
		di_reset();
		break;
	case DI_BWIN2OK:
		di_killwin2(FALSE);
		break;
	case DI_BCOREPIC:
		di_mkwin3((evt->x - di_co_xoff) / di_picsize +
							100 * ((evt->y - di_co_yoff) / di_picsize));
		break;
	case GBUT_NOBUT:
		XBell(dpy, 0);
		break;
	default:
		if (butnum < DI_NBUT)
			di_loadbut(butnum - DI_BPLAY(0));
		else  {
			switch(DI_B3BUT(butnum))  {
			case DI_BWIN3OK:
				di_killwin3(DI_B3WIN(butnum));
				break;
			case DI_BWIN3UP:
				di_dis_newstart(DI_B3WIN(butnum), -1);
				break;
			case DI_BWIN3DOWN:
				di_dis_newstart(DI_B3WIN(butnum), 1);
				break;
			case DI_BWIN3SCROLL:
				scrollpress(DI_B3WIN(butnum), evt->y);
				break;
			}
		}
		break;
	}
}


static void  scrollpress(int winnum, int y)  {
	win3  *w3p = di_win3list.next;
	int  i, sbh, sbindex;
	gbut_info  *info;
	aval_t  newaddr;

	for (i = 0;  i < winnum;  ++i)
		w3p = w3p->next;
	sbh = w3p->scrollbut.h - w3p->scrollbut.w;
	sbindex = (sbh * w3p->startaddr + coresize/2) / coresize;
	if (y < w3p->scrollbut.y + sbindex + di_butbw)  {
		newaddr = w3p->startaddr - w3p->lines;
		if (newaddr > coresize)
			newaddr = 0;
		di_dis_newstart(winnum, newaddr - w3p->startaddr);
	} else if (y > w3p->scrollbut.y + w3p->scrollbut.w + sbindex - di_butbw)  {
		newaddr = w3p->startaddr + w3p->lines;
		if (newaddr >= coresize)
			newaddr = coresize - 1;
		di_dis_newstart(winnum, newaddr - w3p->startaddr);
	}
}


void  di_keypress(Display *dpy, Window win, XKeyPressedEvent *evt)  {
	int  slen;
	char  kbuf[STR_MAXLEN];
	KeySym  keysym;

	slen = XLookupString(evt, kbuf, STR_MAXLEN-1, &keysym, NULL);
	kbuf[slen] = '\0';
	if (kbuf[0] == '\r')
		kbuf[0] = '\n';
	if ((win == di_win2) && (di_win2w != 0) && (kbuf[0] != '\n'))
		di_keypress2(dpy, win, evt);
	else
		gbut_kpress(win, kbuf);
}


void  di_keyrelease(Display *dpy, Window win, XKeyReleasedEvent *evt)  {
	int  butnum, slen;
	char  kbuf[STR_MAXLEN];
	KeySym  keysym;

	slen = XLookupString(evt, kbuf, STR_MAXLEN-1, &keysym, NULL);
	kbuf[slen] = '\0';
	if (kbuf[0] == '\r')
		kbuf[0] = '\n';
	if ((win == di_win2) && (di_win2w != 0) && (kbuf[0] != '\n'))
		return;
	if (XPending(dpy))  {
		XEvent  nextev;

		XPeekEvent(dpy, &nextev);
		if (nextev.type == KeyPress)  {
			if (nextev.xkey.keycode == evt->keycode)  {
				XNextEvent(dpy, &nextev);
				return;
			}
		}
	}
	butnum = gbut_krelease(win, kbuf);
	switch(butnum)  {
	case DI_BSTOP:
		if (di_state == DS_STOPPED)
			di_changestate(dpy, win, DS_STEP);
		else
			di_changestate(dpy, win, DS_STOPPED);
		break;
	case DI_BWIN2OK:
		di_killwin2(FALSE);
		break;
	case DI_BSLOW:
		di_slopos = ((di_but[DI_BSLOW].w - 3) * (kbuf[0] - '1')) / 7;
		/* A logarithmic scale. */
		di_speed = (float)exp((double)di_slopos * log(MAX_SLOW) /
													(double)(di_but[DI_BSLOW].w - 2));
		di_changestate(dpy, win, DS_RUNSLOW);
		di_set_timer(di_speed);
		break;
	case DI_BGO:
		di_changestate(dpy, win, DS_RUNFAST);
		break;
	case DI_BQUIT:
		di_exit(0);
		break;
	case DI_BCOREPIC:
		di_mkwin3(0);
		break;
	case GBUT_NOBUT:
		XBell(dpy, 0);
		break;
	default:
		switch(DI_B3BUT(butnum))  {
		case DI_BWIN3OK:
			di_killwin3(DI_B3WIN(butnum));
			break;
		case DI_BWIN3UP:
			di_dis_newstart(DI_B3WIN(butnum), -1);
			break;
		case DI_BWIN3DOWN:
			di_dis_newstart(DI_B3WIN(butnum), 1);
			break;
		}
		break;
	}
}


void  di_input_m(Display *dpy, Window win, XMotionEvent *evt)  {
	gbut_mmove(win, evt->x, evt->y);
	if ((win == di_win2) && (di_win2w != 0)) {
		if (!di_curon)
			di_cursor(dpy, win, di_curon = TRUE);
	}
}


void  gbut_inbutton(Window win)  {
	if ((last_cursor != LC_INBUT) || (last_win != win))  {
		last_cursor = LC_INBUT;
		last_win = win;
		XDefineCursor(di_dpy, win, inbut_cursor);
	}
}


void  gbut_outbutton(Window win)  {
	if ((last_cursor != LC_NOBUT) || (last_win != win))  {
		last_cursor = LC_NOBUT;
		last_win = win;
		XDefineCursor(di_dpy, win, nobut_cursor);
	}
}


#endif  /* X11_DISP */
