#include <allegro.h>
#include <stdlib.h>
#include <string.h>
#include "gui-mouse.h"
#include "gui-widgets.h"


/*------------------------------------------------------------*/
/* Useful stuff.                                              */
/*------------------------------------------------------------*/

#define rect_wh(b,x,y,w,h,c)		rect(b, x, y, x+w-1, y+h-1, c)
#define rectfill_wh(b,x,y,w,h,c)	rectfill(b, x, y, x+w-1, y+h-1, c)

/*------------------------------------------------------------*/
/* Label.                                                     */
/*------------------------------------------------------------*/

@implementation Label
- (id) init
{
    [super init];
    text_justification = CENTRE_JUSTIFTY;
    draw_background = YES;
    return self;
}

- (id) setDrawBackground:(BOOL)draw { draw_background = draw; return self; }

- (id) setText:(const char *)str
{
    if (text)
	free(text);
    text = strdup(str);
    flags |= FLAG_DIRTY;
    return self;
}

- (id) setTextJustification:(int)new
{
    text_justification = new;
    return self;
}

- (id) free
{
    if (text)
	free(text);
    return [super free];
}
@end

/*------------------------------------------------------------*/
/* Buttons.                                                   */
/*------------------------------------------------------------*/

@implementation Button
- (void) receiveMessage:(int)msg :(int)data
{
    switch (msg) {
      case MSG_KEY_PRESSED:
	  if ((data == KEY_SPACE) || (data == KEY_ENTER))
	      flags |= (FLAG_SELECTED | FLAG_DIRTY);
	  break;

      case MSG_KEY_RELEASED:
	  if ((data == KEY_SPACE) || (data == KEY_ENTER)) {
	      flags &=~FLAG_SELECTED;
	      flags |= FLAG_DIRTY;
	      if (pushed_proc)
		  (*pushed_proc)(self, msg, data);
	  }
	  break;

      case MSG_MOUSE_PRESSED:
	  if (data == 0)
	      [self receiveMessage:MSG_KEY_PRESSED :KEY_SPACE];
	  break;

      case MSG_MOUSE_HELD:
	  if (data == 0) {
	      BOOL old_flags = flags;
	      
	      if (mouse_in(x+h_padding, y+v_padding, w-2*h_padding, h-2*v_padding))
		  flags |= FLAG_SELECTED;
	      else
		  flags &=~FLAG_SELECTED;
	      
	      if (flags != old_flags)
		  flags |= FLAG_DIRTY;
	  }
	  break;

      case MSG_MOUSE_RELEASED:
	  flags &= ~FLAG_SELECTED;
	  if (data == 0)
	      [self receiveMessage:MSG_KEY_RELEASED :KEY_SPACE];
	  break;

      default:
	  [super receiveMessage:msg :data];
    }

    if (parent)
	[parent receiveMessage:msg :data fromChild:self];
}

- (id) setPushedProc:(int (*)(Widget *, int, int))proc { pushed_proc = proc; return self; }
@end


@implementation OkButton
- (void) receiveMessage:(int)msg :(int)data
{
    if ((msg == MSG_MOUSE_RELEASED) && (data == GUI_MOUSE_LMB)) {
	flags &=~(FLAG_SELECTED | FLAG_GOT_FOCUS);
	flags |= FLAG_DIRTY;
	if (parent) 
	    [parent receiveMessage:MSG_OK :data fromChild:self];
    }
    else
	[super receiveMessage:msg :data];
}
@end


@implementation CancelButton
- (void) receiveMessage:(int)msg :(int)data
{
    if ((msg == MSG_MOUSE_RELEASED) && (data == GUI_MOUSE_LMB)) {
	flags &=~(FLAG_SELECTED | FLAG_GOT_FOCUS);
	flags |= FLAG_DIRTY;
	if (parent)
	    [parent receiveMessage:MSG_CANCEL :data fromChild:self];
    }
    else
	[super receiveMessage:msg :data];
}
@end


@implementation ToggleButton
- (void) receiveMessage:(int)msg :(int)data
{
    BOOL old_flags = flags;

    switch (msg) {
	case MSG_KEY_PRESSED:
	    if (data == KEY_SPACE) {
		initiallyToggled = ((flags & FLAG_SELECTED) ? YES : NO);
		flags ^= FLAG_SELECTED;
	    }
	    break;

	case MSG_MOUSE_PRESSED:
	    if (data == GUI_MOUSE_LMB)
		[self receiveMessage: MSG_KEY_PRESSED: KEY_SPACE];
	    break;

	case MSG_MOUSE_HELD:
	    if (data == GUI_MOUSE_LMB) {
		if (mouse_in(x, y, w, h) == initiallyToggled)
		    flags &=~FLAG_SELECTED;
		else
		    flags |= FLAG_SELECTED;
	    }
	    break;

	case MSG_MOUSE_RELEASED:
	    if (pushed_proc)
		(*pushed_proc)(self, msg, flags & FLAG_SELECTED);
	    break;

	default:
	    [super receiveMessage:msg :data];
    }

    if (flags != old_flags)
	flags |= FLAG_DIRTY;
}

//- (id) setPushedProc:(int (*)(Widget *, int, int))proc { pushed_proc = proc; return self; }
@end


@implementation RadioButton
- (id) setGroup:(int)group_ { group = group_; return self; }

- (void) receiveMessage:(int)msg :(int)data
{
    switch (msg) {
	case MSG_KEY_RELEASED:
	    if ((data == KEY_SPACE) && (flags & FLAG_SELECTED)) {
		if (pushed_proc)
		    (*pushed_proc)(self, msg, data);
		//[root_menu receiveMessage: MSG_RADIO_UNTOGGLE: group];
		flags |= FLAG_SELECTED;
	    }
	    break;
    
	case MSG_RADIO_UNTOGGLE:
	    if (data == group) {
		flags &= ~FLAG_SELECTED;
		flags |= FLAG_DIRTY;
	    }
	    break;

	default:
	    [super receiveMessage:msg :data];
    }
}
@end
