/* Program to test the svgalib joystick functions. */
/* Written by M. Weller <eowmob@exp-math.uni-essen.de> */

#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <vga.h>
#include <vgagl.h>
#include <vgamouse.h>
#include <vgajoystick.h>
#include <vgakeyboard.h>

extern char *optarg;
extern int optind, opterr, optopt;

#define PENCILSIZE 5

struct {
    int wx;		/* xoffset to add to scaled joystick position to get cx */
    int cx, cy;		/* onscreen coords of pencil */
    int jx, jy;		/* current joystick status */
    int tx;		/* onscreen xcoord of text for joypos */
    char bitmap[PENCILSIZE * PENCILSIZE * 4]; /* big enough in any screen mode */
    int ox, oy; /* location of saved patch, ox < 0 for no data saved */
    int color;		/* drawing color */
    int drawing;	/* we are drawing (actually shadows button 1 state) */
    int newpos;		/* cx/cy changed, draw new pencil position */
} joypanel[2];

int wy, sx, sy, ty;	/* wy, ty y coords of wx, tx. sx/sy are scale values:
                         * (jx * sx + 128) / 256 is pencil coords (add wx for screen
                         * coords). Same for y. */

int newcolor(void)
{
    if (BYTESPERPIXEL == 1)
	return random() % 15 + 1;
    return gl_rgbcolor(random() & 255, random() & 255, random() & 255);
}

void draw_pencil(int i) {
    char msg[100];

    if (!joypanel[i].newpos)
	return;

    sprintf(msg, "x = %4d, y = %4d", joypanel[i].jx, joypanel[i].jy);
    gl_write(joypanel[i].tx, ty, msg);

    if (joypanel[i].ox >= 0)
	gl_putbox(joypanel[i].ox, joypanel[i].oy, PENCILSIZE, PENCILSIZE, joypanel[i].bitmap);

    /* If not drawing, save destination area */
    if (!joypanel[i].drawing)
	gl_getbox(joypanel[i].ox = joypanel[i].cx, joypanel[i].oy = joypanel[i].cy,
		  PENCILSIZE, PENCILSIZE, joypanel[i].bitmap);
    else
	joypanel[i].ox = -1;
  
    gl_fillbox(joypanel[i].cx, joypanel[i].cy, PENCILSIZE, PENCILSIZE, joypanel[i].color);
    joypanel[i].newpos = 0;
}

void init_screen(void) {
    int white;

    gl_clearscreen(0);

    white = vga_white();

    gl_line(0, 0, WIDTH - 2, 0, white);
    gl_line(0, 0, 0, HEIGHT - 1, white);
    gl_line(WIDTH/2, 0, WIDTH/2, HEIGHT - 1, white);
    gl_line(WIDTH - 2, 0, WIDTH - 2 , HEIGHT - 1, white);
    gl_line(0, 11, WIDTH - 2, 11, white);
    gl_line(0, HEIGHT - 1, WIDTH - 2, HEIGHT - 1, white);

    ty = 2;
    sx = WIDTH / 2 - 3 - PENCILSIZE;
    sy = HEIGHT - 6 - PENCILSIZE - 9;
    wy = 2 + 8 + 3 + (((sy << 7) + 128) >> 8);

    joypanel[0].color = white;
    joypanel[0].drawing = 0;
    joypanel[0].newpos = 1;
    joypanel[0].ox = -1;
    joypanel[0].tx = 2;
    joypanel[0].jx = 0;
    joypanel[0].jy = 0;
    joypanel[0].wx = 2 + (((sx << 7) + 128) >> 8);
    joypanel[0].cx = joypanel[0].wx;
    joypanel[0].cy = wy;

    draw_pencil(0);

    joypanel[1].color = white;
    joypanel[1].drawing = 0;
    joypanel[1].newpos = 1;
    joypanel[1].ox = -1;
    joypanel[1].tx = WIDTH / 2 + 2;
    joypanel[1].jx = 0;
    joypanel[1].jy = 0;
    joypanel[1].wx = joypanel[0].wx + joypanel[1].tx;
    joypanel[1].cx = joypanel[0].cx + joypanel[1].tx;
    joypanel[1].cy = wy;

    draw_pencil(1);
}

void myhandler(int event, int number, char value, int joydev) {
#if 0 
    printf("%d: %d %d %d\n", joydev, event, number, (int)value);
#endif
    switch(event) {
	case JOY_EVENTBUTTONUP:
	    if (!number)
		joypanel[joydev].drawing = 0;
	    else {
		joypanel[joydev].color = newcolor();
		joypanel[joydev].newpos = 1;
	    }
	    break;
	case JOY_EVENTBUTTONDOWN:
	    if (!number)
		joypanel[joydev].drawing = 1;
	    break;
	case JOY_EVENTAXIS:
	    switch(number) {
		case 0: /* x */
		    joypanel[joydev].jx = value;
		    joypanel[joydev].cx = joypanel[joydev].wx + ((((int)value) * sx + 128) / 256);
		    joypanel[joydev].newpos = 1;
		    break;
		case 1:
		    joypanel[joydev].jy = value;
		    joypanel[joydev].cy = wy + ((((int)value) * sy + 128) / 256);
		    joypanel[joydev].newpos = 1;
		    break;
	    }
    }
    /* Note that any reserved events are ignored */
}

void usage(void) {
    puts("Usage: mjoytest [-j <joystick number>] [svgalib mode]\n"
	 "\ttest multiple joystick support and joystick sharing.");
    exit(1);
}

void mycalout(const char *msg) {
    gl_printf(-1, -1, msg);
}

int main(int argc, char *argv[]) {
    int vgamode = -1;
    int which, joymask = 3;
    struct timeval timeout;

    while(EOF != (which = getopt(argc, argv, "j:m:"))) {
	switch(which) {
	    case 'j':
		if (!strcmp(optarg, "0"))
		    joymask = 1;
		else if (!strcmp(optarg, "1"))
		    joymask = 1;
		else
		    usage();
		break;
	    case 'm':
		vgamode = vga_getmodenumber(optarg);
		if (vgamode < 0)
		    usage();
		break;
	    default:
		usage();
	}
    }
    if (optind < argc) {
	if (optind != 1 + argc)
	    usage();
	if (vgamode >= 0)
	    usage();
	vgamode = vga_getmodenumber(argv[optind]);
	if (vgamode < 0)
	    usage();
    }
    vga_init();
    if (vgamode < 0)
	vgamode = vga_getdefaultmode();
    if (vgamode < 0)
	vgamode = G320x200x256;

    if (!vga_hasmode(vgamode)) {
	printf("Mode not available.\n");
	exit(-1);
    }

    puts("In the demo, press\n"
	 "<1> to calibrate joystick 1.\n"
	 "<2> to calibrate joystick 2.\n"
	 "<c> to clear the screen (<1> & <2> do this too).\n"
	 "<q> to exit (<Ctrl>-C should work too.\n"
	 "Joystick button 1 enables drawing while pressed.\n"
	 "Joystick button 2 selects next color.\n"
	 "\nNow hit <Return> to start the demo.");

    getchar();
    fflush(stdin);

    for (which = 0; which < 2; which++) {
	if (!(joymask & (1 << which)))
	    continue;
	errno = 0;
	if (joystick_init(which, JOY_CALIB_STDOUT) < 0) {
	    if (errno)
		printf("Unable to initialize joystick %d: %s.\n", which, strerror(errno));
	    else
		printf("Unable to initialize joystick %d.\n", which);
	}
    }

    joystick_sethandler(-1, myhandler);

    vga_setmode(vgamode);
    gl_setcontextvga(vgamode);

    gl_setwritemode(FONT_COMPRESSED | WRITEMODE_OVERWRITE);
    gl_setfontcolors(0, vga_white());
    gl_setfont(8, 8, gl_font8x8);

    init_screen();

    for(;;) {
	timeout.tv_sec = 0;
	timeout.tv_usec = 10000;
	which = vga_waitevent(VGA_KEYEVENT, NULL, NULL, NULL, &timeout);
	if (which & VGA_KEYEVENT) {
	    switch(vga_getch()) {
		case '1':
		    gl_printf(2, 2, "");
		    if (1 & joymask) {
			vga_lockvc();
			joystick_init(0, mycalout);
		        /* IMPORTANT, reenable ownhandler! */
    		        joystick_sethandler(0, myhandler);
			vga_unlockvc();
		    }
		    init_screen();
		    break;
		case '2':
		    gl_printf(2, 2, "");
		    if (2 & joymask) {
			vga_lockvc();
			joystick_init(1, mycalout);
		        /* IMPORTANT, reenable ownhandler! */
    		        joystick_sethandler(1, myhandler);
			vga_unlockvc();
		    }
		    init_screen();
		    break;
		case 'c':
		case 'C':
		    init_screen();
		    break;
		case 'q':
		case 'Q':
		    goto leave_loop;
		default:
		    putchar('\a');
		    fflush(stdout);
		    break;
	    }
	}
	which = joystick_update();
	if (which & 1);
	    draw_pencil(0); /* It makes only sense to check for the newpos flag
			     * if something happened with the joystick at all */
	if (which & 2);
	    draw_pencil(1);
    }
  leave_loop:
    printf("Shutting down.\n");

    vga_setmode(TEXT);
    exit(0);
}

