/* gui-raid.m,
 *
 * GUI drawing routines for Raid theme.
 */

#include <allegro.h>
#include "common.h"
#ifndef NO_FBLEND
# include "fblend/include/fblend.h"
#endif
#include "gui/gui-raid.h"
#include "seborrhea/seborrhea-allegro.h" /* XXX */


#define DEFAULT_H_PADDING	10
#define DEFAULT_V_PADDING	8
#define BACKGROUND_COLOUR	makecol(0xa0, 0xa0, 0xff)


static inline void backgroundbox(BITMAP *dest, int x, int y, int w, int h, int flags)
{
    int alpha;

    if (flags & FLAG_SELECTED)
	alpha = 0x80;
    else if (flags & FLAG_GOT_FOCUS)
	alpha = 0x60;
    else
	alpha = 0x30;

#ifndef NO_FBLEND
    fblend_rect_trans(dest, x, y, w, h, BACKGROUND_COLOUR, alpha);
#else					/* Normal Allegro routines. */
    drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
    set_trans_blender(0xff, 0xff, 0xff, alpha);
    rectfill(dest, x, y, x+w-1, y+h-1, BACKGROUND_COLOUR);
    drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
#endif					/* End: NO_FBLEND. */
}

static inline void bevelbox(BITMAP *dest, int x, int y, int w, int h, int fg, int bg)
{
    hline(dest, x+1,   y,     x+w-2, fg);
    hline(dest, x,     y+1,   x+w-2, fg);
    hline(dest, x+1,   y+h-2, x+w-1, bg);
    hline(dest, x+1,   y+h-1, x+w-2, bg);
    vline(dest, x,     y+1,   y+h-2, fg);
    vline(dest, x+1,   y,     y+h-2, fg);
    vline(dest, x+w-2, y+1,   y+h-1, bg);
    vline(dest, x+w-1, y+1,   y+h-2, bg);
}

SebFont *default_raid_font;

/*------------------------------------------------------------*/
/* Labels.                                                    */
/*------------------------------------------------------------*/

@implementation RaidLabel
- (id) setFont:(SebFont *)fnt { ttf_font = fnt; return self; }

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    if ([super draw:dest :marked_dirty] == NO)
	return NO;

    if (draw_background) {
	int xx = x+h_padding;
	int yy = y+v_padding;
	int ww = w-2*h_padding;
	int hh = h-2*v_padding;
	backgroundbox(dest, xx+2, yy+2, ww-4, hh-4, flags);
	bevelbox(dest, xx, yy, ww, hh, fg, bg);
    }

    if (text) {
#define col	0x40:0x40:0x40

	SebFont *ttf;
	int yy;

	if (!ttf_font)
	    ttf_font = default_raid_font;
	ttf = (SebFont *)ttf_font;
	[ttf setFontSize:MAX(14, h/3)];
	yy = y+(h-[ttf textHeight])/2;

	switch (text_justification) {
	  case LEFT_JUSTIFY:
	      [ttf putString:text To:dest X:x+h_padding+2 Y:yy Colour:col];
	      break;

	  case CENTRE_JUSTIFTY:
	      [ttf putString:text To:dest X:x+w/2 Y:yy Colour:col Alignment:ALIGN_CENTRE];
	      break;

	  case RIGHT_JUSTIFY:
	      [ttf putString:text To:dest X:x+w-h_padding-2 Y:yy Colour:col Alignment:ALIGN_RIGHT];
	      break;
	}
#undef col
    }

    return YES;
}
@end


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


@implementation RaidButton
- (id) init
{
    [super init];

    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x80, 0x80, 0x80);

    return self;
}

- (id) setFont:(SebFont *)fnt { ttf_font = fnt; return self; }

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    int fg_, bg_;

    if ([super draw:dest :marked_dirty] == NO)
	return NO;

    fg_ = (flags & FLAG_SELECTED) ? bg : fg;
    bg_ = (flags & FLAG_SELECTED) ? fg : bg;

    backgroundbox(dest, x+h_padding+2, y+v_padding+2,
		  w-2*h_padding-4, h-2*v_padding-4, flags);
    bevelbox(dest, x+h_padding, y+v_padding,
	     w-2*h_padding, h-2*v_padding, fg_, bg_);

    return YES;
}
@end


@implementation RaidCheckBox
- (id) init
{
    [super init];

    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x80, 0x80, 0x80);

    button = [[[ToggleButton newWithName:"Check Box Button"]
		  setHPadding:0 VPadding:0]
		 setParentWidget:self];

    label = [[[[[Label newWithName:"Check Box Label"]
		   setHPadding:5 VPadding:0]
		  setTextJustification:LEFT_JUSTIFY]
		 setDrawBackground:NO]
		setParentWidget:self];

    return self;
}

- (id) setX:(int)x_ Y:(int)y_ W:(int)w_ H:(int)h_
{
    int xx, yy, bs;
    [super setX:x_ Y:y_ W:w_ H:h_];

    xx = x + h_padding;
    yy = y + v_padding;
    bs = h - v_padding*2;
    [button setX:xx Y:yy W:bs H:bs];
    [label setX:xx+bs Y:yy W:w-bs-h_padding H:bs];
    return self;
}

- (id) setPushedProc:(int (*)(Widget *, int, int))proc
{
    [button setPushedProc:proc];
    return self;
}

- (Widget *) getWidgetBounding:(int)mx :(int)my
{
    return [button getWidgetBounding:mx :my];
}

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    marked_dirty = [super draw:dest :marked_dirty];

    [button draw:dest :marked_dirty];
    [label  draw:dest :marked_dirty];

    /* Draw an X if the button is selected. */
    if ([button getFlags] & FLAG_SELECTED) {
	int x_, y_, w_, h_, c1, c2;
	[button getX:&x_ Y:&y_ W:&w_ H:&h_];

	c1 = makecol(0xff, 0x60, 0x60);
	c2 = makecol(0xff, 0xa0, 0xa0);

	line(dest, x_+3, y_+3, x_+w_-4, y_+h_-4, c2);
	line(dest, x_+4, y_+3, x_+w_-4, y_+h_-5, c1);
	line(dest, x_+3, y_+4, x_+w_-5, y_+h_-4, c1);
	line(dest, x_+w_-4, y_+3, x_+3, y_+h_-4, c2);
	line(dest, x_+w_-5, y_+3, x_+3, y_+h_-5, c1);
	line(dest, x_+w_-4, y_+4, x_+4, y_+h_-4, c1);
    }

    return YES;
}

- (id) setText:(const char *)str
{
    [label setText:str];
    return self;
}

- (void) receiveMessage:(int)msg :(int)data
{
    if (msg == MSG_REDRAW)
	flags |= FLAG_DIRTY;

    [button receiveMessage:msg :data];
    [label  receiveMessage:msg :data];
}

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

- (ToggleButton *) button { return button; }
@end


/*------------------------------------------------------------*/
/* Sliders.                                                   */
/*------------------------------------------------------------*/


@implementation RaidHSlider
- (id) init
{
    [super init];

    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x80, 0x80, 0x80);

    slider_precision = 100;

    handle_w = 10;
    handle_h = 20;

    return self;
}

- (int) calculateHandleX { return handle_pos * [self sliderLength] / (slider_precision - 1) + [self sliderMin]; }

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    int x_, y_;

    if ([super draw:dest :marked_dirty] == NO)
	return NO;

    y_ = y+h/2;

    if (draw_background) {
	backgroundbox(dest, x+2, y+2, w-4, h-4, flags);
	bevelbox(dest, x, y, w, h, fg, bg);
    }

    {					/* Handle slider. */
	int min = [self sliderMin];
	int max = [self sliderMax];
	hline(dest, x+min,   y_-1, x+max-1, bg);
	hline(dest, x+min+1, y_+1, x+max,   fg);
	putpixel(dest, x+min, y_, bg);
	putpixel(dest, x+max, y_, fg);

	{				/* Slider notches. */
	    double step_size = [self sliderStepSize];

	    for (x_ = min+step_size; x_ < max; x_ += step_size) {
		putpixel(dest, x+x_, y_-2, bg);
		putpixel(dest, x+x_, y_+2, fg);
	    }
	}
    }

    /* Handle. */
    x_ = x+[self calculateHandleX];
    rectfill_wh(dest, x_-handle_w/2+2, y_-handle_h/2+2, handle_w-4, handle_h-4, white);
    bevelbox(dest, x_-handle_w/2, y_-handle_h/2, handle_w, handle_h, fg, bg);

    {					/* Handle grooves. */
	int y1 = y_-handle_h/4;
	int y2 = y_+handle_h/4;
	vline(dest, x_-5, y1, y2, bg); vline(dest, x_-4, y1, y2, fg);
	vline(dest, x_-1, y1, y2, bg); vline(dest, x_,   y1, y2, fg);
	vline(dest, x_+3, y1, y2, bg); vline(dest, x_+4, y1, y2, fg);
    }

    return YES;
}
@end


@implementation RaidVSlider
- (id) init
{
    [super init];

    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x80, 0x80, 0x80);

    slider_precision = 256;

    handle_w = 20;
    handle_h = 10;

    return self;
}

- (int) calculateHandleX { return handle_pos * [self sliderLength] / (slider_precision - 1) + [self sliderMin]; }

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    int x_, y_;

    if ([super draw:dest :marked_dirty] == NO)
	return NO;

    x_ = x+w/2;

    if (draw_background) {
	backgroundbox(dest, x+2, y+2, w-4, h-4, flags);
	bevelbox(dest, x, y, w, h, fg, bg);
    }

    {					/* Handle slider. */
	int min = [self sliderMin];
	int max = [self sliderMax];
	vline(dest, x_-1, y+min,   y+max-1, bg);
	vline(dest, x_+1, y+min+1, y+max, fg);
	putpixel(dest, x_, y+min, bg);
	putpixel(dest, x_, y+max, fg);

	{				/* Slider notches. */
	    double step_size = [self sliderStepSize];

	    for (y_ = min+step_size; y_ < max; y_ += step_size) {
		putpixel(dest, x_-2, y+y_, bg);
		putpixel(dest, x_+2, y+y_, fg);
	    }
	}
    }

    /* Handle. */
    if (slider_precision == 1) {	/* Handle. */
	y_ = y+v_padding;
	rectfill_wh(dest, x_-handle_w/2+2, y_, handle_w-4, h-2*v_padding, white);
	bevelbox(dest, x_-handle_w/2, y_, handle_w, h-2*v_padding, fg, bg);
	y_ = y+h/2;
    }
    else {
	y_ = y+[self calculateHandleX];
	rectfill_wh(dest, x_-handle_w/2+2, y_-handle_h/2+2, handle_w-4, handle_h-4, white);
	bevelbox(dest, x_-handle_w/2, y_-handle_h/2, handle_w, handle_h, fg, bg);
    }

    {					/* Handle grooves. */
	int x1 = x_-handle_w/4;
	int x2 = x_+handle_w/4;
	hline(dest, x1, y_-5, x2, bg); hline(dest, x1, y_-4, x2, fg);
	hline(dest, x1, y_-1, x2, bg); hline(dest, x1, y_,   x2, fg);
	hline(dest, x1, y_+3, x2, bg); hline(dest, x1, y_+4, x2, fg);
    }

    return YES;
}
@end


/*------------------------------------------------------------*/
/* Containers and Combos.                                     */
/*------------------------------------------------------------*/


@implementation RaidHBox
- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    if (backdrop)
	blit([(SebImageAllegro *)backdrop bitmap], dest, 0, 0, x, y, w, h);
    return [super draw:dest :marked_dirty];
}

- (id) setBackdrop:(Sebum<SebImage> *)bmp
{
    backdrop = bmp;
    flags |= FLAG_DIRTY;
    return self;
}

- (BOOL) replaceChild:(int)n With:(Widget *)new_
{
    if (child[n] == new_)
	return NO;
    child[n] = new_;
    [self calculateChildrenDimensions];
    return YES;
}
@end


@implementation RaidVBox
- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    if (backdrop)
	blit([(SebImageAllegro *)backdrop bitmap], dest, 0, 0, x, y, w, h);
    return [super draw:dest :marked_dirty];
}

- (id) setBackdrop:(Sebum<SebImage> *)bmp
{
    backdrop = bmp;
    flags |= FLAG_DIRTY;
    return self;
}

- (BOOL) replaceChild:(int)n With:(Widget *)new_
{
    if (child[n] == new_)
	return NO;
    child[n] = new_;
    [self calculateChildrenDimensions];
    return YES;
}
@end


@implementation RaidHScrollbar
- (id) init
{
    [super init];

    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x80, 0x80, 0x80);

    max_height = 22;

    return self;
}

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    int i;
    marked_dirty = [super draw:dest :marked_dirty];

    [slider draw:dest :marked_dirty];
    for (i = 0; i < SCROLLBAR_NUM_BUTTONS; i++)
	if (button[i])
	    [button[i] draw:dest :marked_dirty];

    return NO;
}
@end


@implementation RaidVScrollbar
- (id) init
{
    [super init];

    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x80, 0x80, 0x80);

    return self;
}

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    int i;
    marked_dirty = [super draw:dest :marked_dirty];

    [slider draw:dest :marked_dirty];
    for (i = 0; i < SCROLLBAR_NUM_BUTTONS; i++)
	if (button[i])
	    [button[i] draw:dest :marked_dirty];

    return NO;
}
@end


@implementation RaidCheckSlider
- (id) init
{
    [super init];

    fg = makecol(0xc0, 0xc0, 0xc0);
    bg = makecol(0x80, 0x80, 0x80);

    button = [[[RaidCheckBox new] setHPadding:5 VPadding:5] setParentWidget:self];
    slider = [[[[[RaidHSlider new] setHPadding:3 VPadding:3]
		   setHandleW:20] setDrawBackground:NO] setParentWidget:self];

    return self;
}

- (id) setText:(const char *)str { if (button) [button setText:str]; return self; }

- (BOOL) draw:(BITMAP *)dest :(BOOL)marked_dirty
{
    marked_dirty = [super draw:dest :marked_dirty];

    backgroundbox(dest, x+2, y+2, w-4, h-4, flags);
    bevelbox(dest, x, y, w, h, fg, bg);

    [button draw:dest :marked_dirty];
    [slider draw:dest :marked_dirty];

    return YES;
}

- (id) setX:(int)x_ Y:(int)y_ W:(int)w_ H:(int)h_
{
    int xx, yy, hh;
    [super setX:x_ Y:y_ W:w_ H:h_];

    xx = x + h_padding;
    yy = y + v_padding;
    hh = h - v_padding*2;
    [[button setX:xx Y:yy W:100 H:hh]
	setHPadding:3 VPadding:1];

    xx = x + 100;
    [slider setX:xx Y:yy W:w-h_padding-100 H:hh];
    return self;
}

- (void) receiveMessage:(int)msg :(int)data
{
    if (msg == MSG_REDRAW)
	flags |= FLAG_DIRTY;

    [button receiveMessage:msg :data];
    [slider receiveMessage:msg :data];
}

- (Widget *) getWidgetBounding:(int)mx :(int)my
{
    if ([super getWidgetBounding:mx :my]) {
	if ([slider getWidgetBounding:mx :my])
	    return slider;

	if ([button getWidgetBounding:mx :my])
	    return button;
    }
    return nil;
}

- (void) receiveMessage:(int)msg :(int)data fromChild:(Widget *)child { (void) msg, (void)data, (void)child; }

- (ToggleButton *) button { return [button button]; }
- (RaidHSlider *) slider { return slider; }
@end

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

void gui_raid_init(void)
{
    [[RaidLabel class] poseAs:[Label class]];
    [[RaidButton class]  poseAs:[Button  class]];
    [[RaidHSlider class] poseAs:[HSlider class]];
    [[RaidVSlider class] poseAs:[VSlider class]];
}
