#include "vterm_internal.h"

#include "utf8.h"

static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row) {
	modifiers <<= 2;

	switch (state->mouse_protocol) {
		case MOUSE_X10:
			if (col + 0x21 > 0xff)
				col = 0xff - 0x21;
			if (row + 0x21 > 0xff)
				row = 0xff - 0x21;

			if (!pressed)
				code = 3;

			vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c", (code | modifiers) + 0x20, col + 0x21, row + 0x21);
			break;

		case MOUSE_UTF8: {
			char   utf8[18];
			size_t len = 0;

			if (!pressed)
				code = 3;

			len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
			len += fill_utf8(col + 0x21, utf8 + len);
			len += fill_utf8(row + 0x21, utf8 + len);
			utf8[len] = 0;

			vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
		} break;

		case MOUSE_SGR:
			vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c", code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
			break;

		case MOUSE_RXVT:
			if (!pressed)
				code = 3;

			vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM", code | modifiers, col + 1, row + 1);
			break;
	}
}

void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod) {
	VTermState *state = vt->state;

	if (col == state->mouse_col && row == state->mouse_row)
		return;

	state->mouse_col = col;
	state->mouse_row = row;

	if ((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
		(state->mouse_flags & MOUSE_WANT_MOVE)) {
		int button = state->mouse_buttons & 0x01 ? 1 :
					 state->mouse_buttons & 0x02 ? 2 :
					 state->mouse_buttons & 0x04 ? 3 :
                                                   4;
		output_mouse(state, button - 1 + 0x20, 1, mod, col, row);
	}
}

void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod) {
	VTermState *state = vt->state;

	int old_buttons = state->mouse_buttons;

	if (button > 0 && button <= 3) {
		if (pressed)
			state->mouse_buttons |= (1 << (button - 1));
		else
			state->mouse_buttons &= ~(1 << (button - 1));
	}

	/* Most of the time we don't get button releases from 4/5 */
	if (state->mouse_buttons == old_buttons && button < 4)
		return;

	if (button < 4) {
		output_mouse(state, button - 1, pressed, mod, state->mouse_col, state->mouse_row);
	} else if (button < 6) {
		output_mouse(state, button - 4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
	}
}