/* Button widget handlers. */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include "elinks.h"

#include "bfu/button.h"
#include "bfu/dialog.h"
#include "intl/gettext/libintl.h"
#include "terminal/draw.h"
#include "terminal/mouse.h"
#include "terminal/terminal.h"
#include "util/align.h"

/* Height of a button. */
#define BUTTON_HEIGHT 1

/* Vertical spacing between buttons. */
#define BUTTON_VSPACING	1

/* Horizontal spacing between buttons. */
#define BUTTON_HSPACING	2

/* Left and right text appearing around label of button.
 * Currently a dialog button is displayed as [ LABEL ] */
#define BUTTON_LEFT "[ "
#define BUTTON_RIGHT " ]"
#define BUTTON_LEFT_LEN (sizeof(BUTTON_LEFT) - 1)
#define BUTTON_RIGHT_LEN (sizeof(BUTTON_RIGHT) - 1)

#define BUTTON_LR_LEN (BUTTON_LEFT_LEN + BUTTON_RIGHT_LEN)

#ifdef DEBUG_BUTTON_HOTKEY
void
add_dlg_button_do(const char *file, int line,
		  struct dialog *dlg, char *text, int flags,
		  widget_handler_T *handler, void *data,
		  done_handler_T *done, void *done_data)
#else
void
add_dlg_button_do(struct dialog *dlg, char *text, int flags,
		  widget_handler_T *handler, void *data,
		  done_handler_T *done, void *done_data)
#endif
{
	int textlen = strlen(text);
	struct widget *widget = &dlg->widgets[dlg->number_of_widgets++];

	widget->type	= WIDGET_BUTTON;
	widget->handler	= handler;
	widget->text	= text;
	widget->data	= data;

	widget->info.button.flags       = flags;
	widget->info.button.done        = done;
	widget->info.button.done_data   = done_data;
	widget->info.button.hotkey_pos  = -1;
	widget->info.button.textlen     = textlen;
	widget->info.button.truetextlen = textlen;

	if (textlen > 1) {
		char *pos = memchr(text, '~', textlen - 1);

		if (pos) {
			widget->info.button.hotkey_pos = pos - text;
			widget->info.button.textlen--;
		}
#ifdef DEBUG_BUTTON_HOTKEY
		else {
			DBG("%s:%d missing keyboard accelerator in \"%s\".", file, line, text);
		}
#endif
	}
}

#ifdef CONFIG_UTF8
static void
buttons_width(struct widget_data *widget_data, int n,
	      int *minwidth, int *maxwidth, int utf8)
#else
static void
buttons_width(struct widget_data *widget_data, int n,
	      int *minwidth, int *maxwidth)
#endif /* CONFIG_UTF8 */
{
	int maxw = -BUTTON_HSPACING;
#ifdef CONFIG_UTF8
	int button_lr_len = utf8_ptr2cells(BUTTON_LEFT, NULL)
			  + utf8_ptr2cells(BUTTON_RIGHT, NULL);
#endif /* CONFIG_UTF8 */

	assert(n > 0);
	if_assert_failed return;

	while (n--) {
		int minw;
#ifdef CONFIG_UTF8
		if (utf8)
			minw = utf8_ptr2cells((widget_data++)->widget->text, NULL)
			       + BUTTON_HSPACING + button_lr_len;
		else
#endif /* CONFIG_UTF8 */
			minw = (widget_data++)->widget->info.button.textlen
				+ BUTTON_HSPACING + BUTTON_LR_LEN;

		maxw += minw;
		if (minwidth) int_lower_bound(minwidth, minw);
	}

	if (maxwidth) int_lower_bound(maxwidth, maxw);
}

void
dlg_format_buttons(struct dialog_data *dlg_data,
		   struct widget_data *widget_data, int n,
		   int x, int *y, int w, int *rw, enum format_align align, int format_only)
{
#ifdef CONFIG_UTF8
	struct terminal *term = dlg_data->win->term;
#endif
	int i1 = 0;

	while (i1 < n) {
		struct widget_data *widget_data1 = widget_data + i1;
		int i2 = i1 + 1;
		int mw;

		while (i2 < n) {
			mw = 0;
#ifdef CONFIG_UTF8
			buttons_width(widget_data1, i2 - i1 + 1, NULL, &mw,
				      term->utf8_cp);
#else
			buttons_width(widget_data1, i2 - i1 + 1, NULL, &mw);
#endif /* CONFIG_UTF8 */
			if (mw <= w) i2++;
			else break;
		}

		mw = 0;
#ifdef CONFIG_UTF8
		buttons_width(widget_data1, i2 - i1, NULL, &mw, term->utf8_cp);
#else
		buttons_width(widget_data1, i2 - i1, NULL, &mw);
#endif /* CONFIG_UTF8 */
		if (rw) int_bounds(rw, mw, w);

		if (!format_only) {
			int i;
			int p = x + (align == ALIGN_CENTER ? (w - mw) / 2 : 0);
#ifdef CONFIG_UTF8
			int button_lr_len = utf8_ptr2cells(BUTTON_LEFT, NULL)
					  + utf8_ptr2cells(BUTTON_RIGHT, NULL);
#endif /* CONFIG_UTF8 */

			for (i = i1; i < i2; i++) {
#ifdef CONFIG_UTF8
				if (term->utf8_cp)
					set_box(&widget_data[i].box,
						p, *y,
						utf8_ptr2cells(widget_data[i].widget->text, NULL)
						+ button_lr_len, BUTTON_HEIGHT);
				else
#endif /* CONFIG_UTF8 */
					set_box(&widget_data[i].box,
						p, *y,
						widget_data[i].widget->info.button.textlen
						+ BUTTON_LR_LEN, BUTTON_HEIGHT);

				p += widget_data[i].box.width + BUTTON_HSPACING;
			}
		}

		*y += BUTTON_VSPACING + BUTTON_HEIGHT;
		i1 = i2;
	}
}

static widget_handler_status_T
display_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
{
	struct terminal *term = dlg_data->win->term;
	struct color_pair *color, *shortcut_color;
	struct el_box *pos = &widget_data->box;
	int len, x;
	int sel = is_selected_widget(dlg_data, widget_data);

	if (sel) {
		shortcut_color = get_bfu_color(term, "dialog.button-shortcut-selected");
		color =  get_bfu_color(term, "dialog.button-selected");
	} else {
		shortcut_color = get_bfu_color(term, "dialog.button-shortcut");
		color =  get_bfu_color(term, "dialog.button");
	}
	if (!color || !shortcut_color) return EVENT_PROCESSED;

#ifdef CONFIG_UTF8
	if (term->utf8_cp) {
		int button_left_len = utf8_ptr2cells(BUTTON_LEFT, NULL);
		int button_right_len = utf8_ptr2cells(BUTTON_RIGHT, NULL);

		x = pos->x + button_left_len;
		len = widget_data->box.width -
			(button_left_len + button_right_len);

	} else
#endif /* CONFIG_UTF8 */
	{
		x = pos->x + BUTTON_LEFT_LEN;
		len = widget_data->box.width - BUTTON_LR_LEN;
	}


	draw_dlg_text(dlg_data, pos->x, pos->y, BUTTON_LEFT, BUTTON_LEFT_LEN, 0, color);
	if (len > 0) {
		char *text = widget_data->widget->text;
		int hk_pos = widget_data->widget->info.button.hotkey_pos;
		int attr;

		attr = get_opt_bool("ui.dialogs.underline_button_shortcuts",
		                    NULL)
		     ? SCREEN_ATTR_UNDERLINE : 0;

#ifdef CONFIG_UTF8
		if (term->utf8_cp) {
			if (hk_pos >= 0) {
				int hk_bytes = utf8charlen(&text[hk_pos+1]);
				int cells_to_hk = utf8_ptr2cells(text,
						&text[hk_pos]);
				int right = widget_data->widget->info.button.truetextlen
					- hk_pos
					- hk_bytes;

				int hk_cells = utf8_char2cells(&text[hk_pos
								      + 1],
								NULL);

				if (hk_pos)
					draw_dlg_text(dlg_data, x, pos->y,
						  text, hk_pos, 0, color);

				draw_dlg_text(dlg_data, x + cells_to_hk, pos->y,
					  &text[hk_pos + 1], hk_bytes,
					  attr, shortcut_color);

				if (right > 1)
					draw_dlg_text(dlg_data, x+cells_to_hk+hk_cells,
						  pos->y,
						  &text[hk_pos + hk_bytes + 1],
						  right - 1, 0, color);

			} else {
				int hk_width = utf8_char2cells(text, NULL);
				int hk_len = utf8charlen(text);
				int len_to_display =
					utf8_cells2bytes(&text[hk_len],
							 len - hk_width,
							 NULL);

				draw_dlg_text(dlg_data, x, pos->y,
					  text, hk_len,
					  attr, shortcut_color);

				draw_dlg_text(dlg_data, x + hk_width, pos->y,
					  &text[hk_len], len_to_display,
					  0, color);
			}
		} else
#endif /* CONFIG_UTF8 */
		if (hk_pos >= 0) {
			int right = widget_data->widget->info.button.truetextlen - hk_pos - 1;

			if (hk_pos) {
				draw_dlg_text(dlg_data, x, pos->y, text, hk_pos, 0, color);
			}
			draw_dlg_text(dlg_data, x + hk_pos, pos->y,
				  &text[hk_pos + 1], 1, attr, shortcut_color);
			if (right > 1) {
				draw_dlg_text(dlg_data, x + hk_pos + 1, pos->y,
					  &text[hk_pos + 2], right - 1, 0, color);
			}

		} else {
			draw_dlg_text(dlg_data, x, pos->y, text, 1, attr, shortcut_color);
			draw_dlg_text(dlg_data, x + 1, pos->y, &text[1], len - 1, 0, color);
		}
	}
#ifdef CONFIG_UTF8
	if (term->utf8_cp) {
		int text_cells = utf8_ptr2cells(widget_data->widget->text, NULL);
		int hk = (widget_data->widget->info.button.hotkey_pos >= 0);

		draw_dlg_text(dlg_data, x + text_cells - hk, pos->y,
			  BUTTON_RIGHT, BUTTON_RIGHT_LEN, 0, color);
	} else
#endif /* CONFIG_UTF8 */
		draw_dlg_text(dlg_data, x + len, pos->y, BUTTON_RIGHT,
			  BUTTON_RIGHT_LEN, 0, color);
	if (sel) {
		set_dlg_cursor(term, dlg_data, x, pos->y, 1);
		set_dlg_window_ptr(dlg_data, dlg_data->win, pos->x, pos->y);
	}
	return EVENT_PROCESSED;
}

static widget_handler_status_T
mouse_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
{
	struct terminal *term = dlg_data->win->term;
	struct term_event *ev = dlg_data->term_event;

	if (check_mouse_wheel(ev))
		return EVENT_NOT_PROCESSED;

	if (!check_mouse_position(ev, &widget_data->box))
		return EVENT_NOT_PROCESSED;

	select_widget(dlg_data, widget_data);

	do_not_ignore_next_mouse_event(term);

	if (check_mouse_action(ev, B_UP) && widget_data->widget->ops->select)
		return widget_data->widget->ops->select(dlg_data, widget_data);

	return EVENT_PROCESSED;
}

static widget_handler_status_T
select_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
{
	return widget_data->widget->handler(dlg_data, widget_data);
}

const struct widget_ops button_ops = {
	display_button,
	NULL,
	mouse_button,
	NULL,
	select_button,
	NULL,
};
