#include <allegro.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#define check_init(func,parms)				\
if (func parms < 0) {					\
	fprintf(stderr, #func ": %s\n", allegro_error);	\
	return EXIT_FAILURE;				\
}

/* Cribbed from guiproc.c */
static void dotted_rect(int x1, int y1, int x2, int y2, int fg, int bg)
{
   int x = ((x1+y1) & 1) ? 1 : 0;
   int c;

   /* two loops to avoid bank switches */
   for (c=x1; c<=x2; c++)
      putpixel(screen, c, y1, (((c+y1) & 1) == x) ? fg : bg);
   for (c=x1; c<=x2; c++)
      putpixel(screen, c, y2, (((c+y2) & 1) == x) ? fg : bg);

   for (c=y1+1; c<y2; c++) {
      putpixel(screen, x1, c, (((c+x1) & 1) == x) ? fg : bg);
      putpixel(screen, x2, c, (((c+x2) & 1) == x) ? fg : bg);
   }
}

/* gui proc to trap dragging nothing */
int my_grab_proc(int msg, DIALOG* d, int c)
{
	switch (msg) {
	case MSG_START:
		d->x = d->y = 0; d->w = SCREEN_W; d->h = SCREEN_H;
		return D_O_K;
	case MSG_CLICK:
		return D_WANTDRAG;
	default:
		return D_O_K;
	}
}

/* gui proc to prove a point */
int my_blinkenlight(int msg, DIALOG* d, int c)
{
	if (msg == MSG_DRAW) {
		circlefill(screen, d->x, d->y, d->w - 1, d->d1 ?
				gui_mg_color : d->bg);
		circle(screen, d->x, d->y, d->w, d->fg);
	}

	return D_O_K;
}

/* gui proc to demonstrate dragging */
int my_drag_proc(int msg, DIALOG* d, int c)
{
	int l = d->x, t = d->y, r = l + d->w - 1, b = t + d->h - 1; 
	int cx = (l + r) / 2, cy = (t + b) / 2;
	switch (msg) {
	case MSG_DRAW:
		if (d->flags & D_DRAGGED) {
			rect(screen, l, t, r, b, d->fg);
			rect(screen, l + 1, t + 1, r - 1, b - 1, d->fg);
			rectfill(screen, l + 2, t + 2, r - 2, b - 2, d->bg);

			circlefill(screen, cx, cy, MIN(d->w, d->h) / 4, d->fg);
			circlefill(screen, cx, cy, MIN(d->w, d->h) / 4 - 3,
					d->bg);

			hline(screen, l + 3, cy, r - 3, d->fg);
			vline(screen, cx, t + 3, b - 3, d->fg);

			line(screen, l + 4, cy, l + 10, cy - 3, d->fg);
			line(screen, l + 4, cy, l + 10, cy + 3, d->fg);

			line(screen, r - 4, cy, r - 10, cy - 3, d->fg);
			line(screen, r - 4, cy, r - 10, cy + 3, d->fg);

			line(screen, cx, t + 4, cx - 3, t + 10, d->fg);
			line(screen, cx, t + 4, cx + 3, t + 10, d->fg);

			line(screen, cx, b - 4, cx - 3, b - 10, d->fg);
			line(screen, cx, b - 4, cx + 3, b - 10, d->fg);
		} else if (d->flags & D_GOTMOUSE) {
			rect(screen, l, t, r, b, d->fg);
			dotted_rect(l + 1, t + 1, r - 1, b - 1, d->fg, d->bg);
			rectfill(screen, l + 2, t + 2, r - 2, b - 2, d->bg);
			circlefill(screen, cx, cy, MIN(d->w, d->h) / 4, d->fg);
			circlefill(screen, cx, cy, MIN(d->w, d->h) / 4 - 2,
					d->bg);
		} else {
			rect(screen, l, t, r, b, d->fg);
			rectfill(screen, l + 1, t + 1, r - 1, b - 1, d->bg);
			circle(screen, cx, cy, MIN(d->w, d->h) / 4, d->fg);
		}
		return D_O_K;
	/* This means any mouse button has been pressed */
	case MSG_CLICK:
		return D_REDRAWME | D_WANTDRAG;
	case MSG_LRELEASE:
	case MSG_MRELEASE:
	case MSG_RRELEASE:
	case MSG_GOTMOUSE:
	case MSG_LOSTMOUSE:
		return D_REDRAWME;
	case MSG_DRAG:
		d->x += (short)(c >> 16);
		d->y += (short)(c & 0xffff);
		return D_REDRAW;
	default:
		return D_O_K;
	}
}

/* my_button_proc:
 *  A button object (the dp field points to the text string). This object
 *  can be selected by clicking on it with the mouse or by pressing its 
 *  keyboard shortcut. If the D_EXIT flag is set, selecting it will close 
 *  the dialog, otherwise it will toggle on and off.
 */

/* This is my drag-enabled modification */
int my_button_proc(int msg, DIALOG *d, int c)
{
   int black;
   int rtm;
   int l, t, r, b, cx, cy;
   int state1, state2;

   switch (msg) {

      case MSG_DRAW:
	 if (d->flags & D_SELECTED) {
	    l = d->x + 1; t = d->y + 1;
	    state1 = d->bg;
	    state2 = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
	 }
	 else {
	    l = d->x; t = d->y;
	    state1 = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
	    state2 = d->bg;
	 }
	 r = l + d->w - 2; b = t + d->h - 2;
	 cx = (l + r) / 2; cy = (t + b - text_height(font)) / 2 + 1;

	 rectfill(screen, l + 1, t + 1, r - 1, b - 1, state2);
	 rect(screen, l, t, r, b, state1);
	 rtm = text_mode(-1);
	 gui_textout(screen, d->dp, cx, cy, state1, TRUE);
	 text_mode(rtm);

	 if (d->flags & D_SELECTED) {
	    hline(screen, l - 1, t - 1, r - 1, d->bg);
	    vline(screen, l - 1, t - 1, b - 1, d->bg);
	 }
	 else {
	    black = makecol(0,0,0);
	    hline(screen, l + 1, b + 1, r + 1, black);
	    vline(screen, r + 1, t + 1, b + 1, black);
	 }
	 if ((d->flags & D_GOTFOCUS) && 
	     (!(d->flags & D_SELECTED) || !(d->flags & D_EXIT)))
	    dotted_rect(l + 1, t + 1, r - 1, b - 1, state1, state2);
	 break;

      case MSG_WANTFOCUS:
	 return D_WANTFOCUS;

      case MSG_KEY:
	 /* close dialog? */
	 if (d->flags & D_EXIT) {
	    return D_CLOSE;
	 }

	 /* or just toggle */
	 d->flags ^= D_SELECTED;
	 return D_REDRAWME;

      case MSG_CLICK:
	 /* what state was the button originally in? */
	 /* D_INTERNAL is used to remember the original state */
	 if (d->flags & D_EXIT)
            d->flags &= ~D_SELECTED;
	 if (d->flags & D_SELECTED)
	    d->flags |= D_INTERNAL;
	 else
            d->flags &= ~D_INTERNAL;
	 return D_WANTDRAG | D_REDRAWME;

      case MSG_DRAG:
	 /* track the mouse until it is released */
	 state1 = d->flags & D_INTERNAL;
	 state2 = ((gui_mouse_x() >= d->x) && (gui_mouse_y() >= d->y) &&
	    (gui_mouse_x() < d->x + d->w) && (gui_mouse_y() < d->y + d->h));
	 if (d->flags & D_SELECTED)
	    state2 = !state2;

	 /* redraw? */
	 if (((state1) && (!state2)) || ((state2) && (!state1))) {
	    d->flags ^= D_SELECTED;
	    scare_mouse();
	    object_message(d, MSG_DRAW, 0);
	    unscare_mouse();
	 }
	 break;

      case MSG_LRELEASE:
      case MSG_MRELEASE:
      case MSG_RRELEASE:
	 /* should we close the dialog? */
	 if ((d->flags & D_SELECTED) && (d->flags & D_EXIT)) {
	    d->flags ^= D_SELECTED;
	    return D_CLOSE;
	 }
	 break; 
   }

   return D_O_K;
}

/* A way of keeping the my_dialog definition more concise */
static char title[] = "Andy's Test Thing";
static char b1[] = "Old Button";
static char b2[] = "New Button";
static char b3[] = "E&xit";
static char r1[] = "Radio #1";
static char r2[] = "Radio #2";
static char r3[] = "Radio #3";
static char c1[] = "Check #1";
static char c2[] = "Check #2";
static char c3[] = "Check #3";
static char edit[81] = "Foo";

static DIALOG my_dialog[] = {
/* dialog proc       x   y   w   h fgbgkey  flags  d1 d2   dp p2p3 */
{d_clear_proc,       0,  0,  0,  0,0,0,  0,     0,  0,0,    0,0,0},
{d_yield_proc,       0,  0,  0,  0,0,0,  0,     0,  0,0,    0,0,0},
{my_grab_proc,       0,  0,  0,  0,0,0,  0,     0,  0,0,    0,0,0},
{d_shadow_box_proc,100,100,440,200,0,0,  0,     0,  0,0,    0,0,0},
{d_ctext_proc,     320,108,  0,  0,0,0,  0,     0,  0,0,title,0,0},
{d_button_proc,    110,130,120, 24,0,0,  0,     0,  0,0,   b1,0,0},
{my_button_proc,   240,130,120, 24,0,0,  0,     0,  0,0,   b2,0,0},
{d_radio_proc,     110,160,120, 11,0,0,  0,     0,  1,0,   r1,0,0},
{d_radio_proc,     110,185,120, 11,0,0,  0,     0,  1,0,   r2,0,0},
{d_radio_proc,     110,210,120, 11,0,0,  0,     0,  1,0,   r3,0,0},
{d_check_proc,     240,160,120, 11,0,0,  0,     0,  0,0,   c1,0,0},
{d_check_proc,     240,185,120, 11,0,0,  0,     0,  0,0,   c2,0,0},
{d_check_proc,     240,210,120, 11,0,0,  0,     0,  0,0,   c3,0,0},
{d_edit_proc,      410,130,120, 10,0,0,  0,     0, 80,0, edit,0,0},
{d_slider_proc,    110,240,250, 20,0,0,  0,     0,100,0,    0,0,0}, 
{my_button_proc,   480,260, 50, 24,0,0,'x',D_EXIT,  0,0,   b3,0,0},
{my_drag_proc,     430,150, 49, 49,0,0,  0,     0,  0,0,    0,0,0},
{my_blinkenlight,  110,110,  4,  4,0,0,  0,     0,  0,0,    0,0,0},
{NULL,               0,  0,  0,  0,0,0,  0,     0,  0,0,    0,0,0}
};

int main(void)
{    
	DIALOG_PLAYER* p;
	DIALOG* w;
	BITMAP* buf;
	BITMAP* real_screen;

	/* Initialize Allegro */
	check_init(install_allegro, (SYSTEM_AUTODETECT, &errno, atexit));
	check_init(install_timer, ());
	check_init(install_keyboard, ());
	check_init(install_mouse, ());
	set_color_depth(32);
	check_init(set_gfx_mode, (GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0));
	set_uformat(U_ASCII);
	timer_simulate_retrace(1);
	buf = create_bitmap(SCREEN_W, SCREEN_H);
	show_mouse(buf);

	/* Select the first radio button */
	for (w = &my_dialog[0]; w->proc != d_radio_proc; w++);
	w->flags |= D_SELECTED;

	/* Adjust the dialogue colors */
	gui_bg_color = makecol(0xef, 0xef, 0xef);
	gui_mg_color = makecol(0x8f, 0x8f, 0x8f);
	gui_fg_color = makecol(0x5f, 0x5f, 0x5f);
	set_dialog_color(my_dialog, gui_fg_color, gui_bg_color);

	/* Start the gui */
	real_screen = screen; screen = buf;
	p = init_dialog(my_dialog, -1);

	/* Locate the light */
	for (w = &my_dialog[0]; w->proc != my_blinkenlight; w++);

	/* My do_dialog */
	while (update_dialog(p)) {
		/* Animate the blinking light */
		if (retrace_count >= 17) {
			w->d1 ^= (retrace_count / 17) & 1;
			scare_mouse();
			object_message(w, MSG_DRAW, 0);
			unscare_mouse();
			retrace_count = 0;
		}

		/* Update the screen from the double buffer */
		blit(buf, real_screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
	}

	/* Done! */
	show_mouse(NULL);
	screen = real_screen;
	destroy_bitmap(buf);
	return shutdown_dialog(p);
}
END_OF_MAIN();

