#include <allegro.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "gui.h"
#include "gui-clip.h"
#include "gui-dirty.h"
#include "gui-keyboard.h"
#include "gui-mouse.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)

int black, blue, green, red, white;
int cyan, magenta, yellow;
int pawblue, pawgreen, pawpink;

/*------------------------------------------------------------*/
/* Common routines.                                           */
/*------------------------------------------------------------*/

int gui_init(void)
{
    if ((gui_keyboard_init() < 0) ||
	(gui_mouse_init() < 0))
	return -1;

    black   = makecol(0x00, 0x00, 0x00);
    blue    = makecol(0x00, 0x00, 0xff);
    green   = makecol(0x00, 0xff, 0x00);
    red     = makecol(0xff, 0x00, 0x00);
    white   = makecol(0xff, 0xff, 0xff);

    cyan    = makecol(0x00, 0xff, 0xff);
    magenta = makecol(0xff, 0x00, 0xff);
    yellow  = makecol(0xff, 0xff, 0x00);

    pawblue = makecol(0xa0, 0xa0, 0xff);
    pawgreen= makecol(0xa0, 0xff, 0xa0);
    pawpink = makecol(0xff, 0xa0, 0xa0);

    return 0;
}

void gui_shutdown(void)
{
    gui_mouse_shutdown();
    gui_keyboard_shutdown();
}

/*------------------------------------------------------------*/
/* The most basic widget.                                     */
/*------------------------------------------------------------*/

@implementation Widget
	/* Initialization. */
+ (id) newWithName:(const char *)name_
{
    return [[self alloc] initWithName:name_];
}

- (id) init
{
    [super init];
    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x60, 0x60, 0x60);
    w = h = 1;
    min_width = min_height = 1;
    max_width = max_height = 0;
    h_padding = v_padding = 0;

    flags |= FLAG_DIRTY;
    return self;
}

- (id) initWithName:(const char *)name_
{
    [self init];
    name = strdup(name_);
    return self;
}

	/* Dimensions. */
- (id) setToMinDimensions  { w = [self minWidth]; h = [self minHeight]; return self; }
- (id) setMinWidth:(int)w_ { min_width  = MAX(1, w_); return self; }
- (id) setMinHeight:(int)h_{ min_height = MAX(1, h_); return self; }
- (id) setMaxWidth:(int)w_ { max_width  = w_; return self; }
- (id) setMaxHeight:(int)h_{ max_height = h_; return self; }
- (id) setMinWidth:(int)w_ Height:(int)h_ { return [[self setMinWidth:w_] setMinHeight:h_]; }
- (id) setMaxWidth:(int)w_ Height:(int)h_ { return [[self setMaxWidth:w_] setMaxHeight:h_]; }

	/* Other props. */
- (id) setFG:(int)fg_ { fg = fg_; return self; }
- (id) setBG:(int)bg_ { bg = bg_; return self; }
- (id) setFG:(int)fg_ BG:(int)bg_ { return [[self setFG:fg_] setBG:bg_]; }

- (id) setFlag:(int)flag
{
    flags |= flag;
    return self;
}

- (id) unsetFlag:(int)flag
{
    flags &= ~flag;
    return self;
}

- (id) free
{
    if (name) free(name);
    return [super free];
}

- (id) setHPadding:(int)hp VPadding:(int)vp
{
    h_padding = MAX(0, hp);
    v_padding = MAX(0, vp);
    return self;
}

- (id) setParentWidget:(Widget<Container> *)widget { parent = widget; return self; }

/* Other stuff. */
- (BOOL) draw:(BITMAP *)bmp :(BOOL)marked_dirty
{
    (void)bmp;

    if (!(flags & FLAG_DIRTY))
	return marked_dirty;

    flags &=~FLAG_DIRTY;
    if (!marked_dirty) {
	/* The parent didn't mark the space dirty - DIY. */
	add_dirty_rectangle(x, y, w, h);
    }

    /* Tell sub-classes to draw. */
    return YES;
}

- (void) receiveMessage:(int)msg { [self receiveMessage:msg :0]; }
- (void) receiveMessage:(int)msg :(int)data
{
    if (msg == MSG_REDRAW)
	flags |= FLAG_DIRTY;
    else if (msg == MSG_MOUSE_ENTER) {
	flags |= FLAG_DIRTY;
	flags |= FLAG_GOT_FOCUS;
    }
    else if (msg == MSG_MOUSE_EXIT) {
	flags |= FLAG_DIRTY;
	flags &= ~FLAG_GOT_FOCUS;
    }

    (void)data;
}


/* Internal functions. */
- (int) width  { return w; }
- (int) height { return h; }
- (int) minWidth  { return min_width;  }
- (int) minHeight { return min_height; }
- (int) maxWidth  { return max_width;  }
- (int) maxHeight { return max_height; }
- (int) getFlags { return flags; }

- (Widget *) getWidgetBounding:(int)mx :(int)my
{
    if ((x < mx) && (x + w > mx) && (y < my) && (y + h > my))
	return self;
    return nil;
}

- (id) getX:(int *)x_ Y:(int *)y_ W:(int *)w_ H:(int *)h_
{
    (*x_) = x;
    (*y_) = y;
    (*w_) = MAX(1, w);
    (*h_) = MAX(1, h);
    return self;
}

- (id) setX:(int)x_ Y:(int)y_ W:(int)w_ H:(int)h_
{
    if (x_ >= 0) x = x_;
    if (y_ >= 0) y = y_;
    if (w_ >  0) { w = MAX(w_, [self minWidth]);  if (max_width  > 0) w = MIN(w, max_width); }
    if (h_ >  0) { h = MAX(h_, [self minHeight]); if (max_height > 0) h = MIN(h, max_height); }

    if (w < w_) x += (w_ - w) / 2;
    if (h < h_) y += (h_ - h) / 2;

    return self;
}

- (int) incrementW:(int)w_
{
    /* Returns the amount of space used. */
    if ((max_width > 0) && (w + w_ >= max_width)) {
	int ret = max_width - w;
	w = max_width;
	return ret;
    }

    w += w_;
    return w_;
}

- (int) incrementH:(int)h_
{
    if ((max_height > 0) && (h + h_ >= max_height)) {
	int ret = max_height - h;
	h = max_height;
	return ret;
    }

    h += h_;
    return h_;
}
@end


/*------------------------------------------------------------*/

@implementation Frame
- (id) free
{
    if (child) child = [child free];
    return [super free];
}

- (id) setChild:(Widget *)widget { child = widget; [widget setParentWidget:(Widget *)self]; return self; }
- (id) setX:(int)x_ Y:(int)y_ W:(int)w_ H:(int)h_ { if (child) [child setX:x_ Y:y_ W:w_ H:h_]; return self; }

- (void) draw:(BITMAP *)dest
{
    if (child)
	[child draw:dest :NO];
    gui_update_dirty(dest);
}

- (int) update
{
    messages = 0;
    gui_update_keyboard(focus_object);
    gui_update_mouse(child, &focus_object);
    yield_timeslice();		/* XXX: Does this do anything? */

    if ((messages == MSG_OK) ||
	(messages == MSG_CANCEL))
	focus_object = nil;

    return messages;
}

- (void) receiveMessage:(int)msg :(int)data
{
    if (child)
	[child receiveMessage:msg :data];
}

- (void) receiveMessage:(int)msg :(int)data fromChild:(Widget *)widget
{
    (void)data, (void)widget;
    messages = msg;
}
@end
