#include "vis-core.h"
#include "text-objects.h"
#include "util.h"

int vis_textobject_register(Vis *vis, int type, void *data, VisTextObjectFunction *textobject) {

	TextObject *obj = calloc(1, sizeof *obj);
	if (!obj)
		return -1;

	obj->user = textobject;
	obj->type = type;
	obj->data = data;

	if (array_add_ptr(&vis->textobjects, obj))
		return LENGTH(vis_textobjects) + array_length(&vis->textobjects) - 1;
	free(obj);
	return -1;
}

bool vis_textobject(Vis *vis, enum VisTextObject id) {
	if (id < LENGTH(vis_textobjects))
		vis->action.textobj = &vis_textobjects[id];
	else
		vis->action.textobj = array_get_ptr(&vis->textobjects, id - LENGTH(vis_textobjects));
	if (!vis->action.textobj)
		return false;
	vis_do(vis);
	return true;
}

static Filerange search_forward(Vis *vis, Text *txt, size_t pos) {
	Filerange range = text_range_empty();
	Regex *regex = vis_regex(vis, NULL);
	if (regex)
		range = text_object_search_forward(txt, pos, regex);
	text_regex_free(regex);
	return range;
}

static Filerange search_backward(Vis *vis, Text *txt, size_t pos) {
	Filerange range = text_range_empty();
	Regex *regex = vis_regex(vis, NULL);
	if (regex)
		range = text_object_search_backward(txt, pos, regex);
	text_regex_free(regex);
	return range;
}

static Filerange object_unpaired(Text *txt, size_t pos, char obj) {
	char c;
	bool before = false;
	Iterator it = text_iterator_get(txt, pos), rit = it;

	while (text_iterator_byte_get(&rit, &c) && c != '\n') {
		if (c == obj) {
			before = true;
			break;
		}
		text_iterator_byte_prev(&rit, NULL);
	}

	/* if there is no previous occurrence on the same line, advance starting position */
	if (!before) {
		while (text_iterator_byte_get(&it, &c) && c != '\n') {
			if (c == obj) {
				pos = it.pos;
				break;
			}
			text_iterator_byte_next(&it, NULL);
		}
	}

	switch (obj) {
	case '"':
		return text_object_quote(txt, pos);
	case '\'':
		return text_object_single_quote(txt, pos);
	case '`':
		return text_object_backtick(txt, pos);
	default:
		return text_range_empty();
	}
}

static Filerange object_quote(Text *txt, size_t pos) {
	return object_unpaired(txt, pos, '"');
}

static Filerange object_single_quote(Text *txt, size_t pos) {
	return object_unpaired(txt, pos, '\'');
}

static Filerange object_backtick(Text *txt, size_t pos) {
	return object_unpaired(txt, pos, '`');
}

const TextObject vis_textobjects[] = {
	[VIS_TEXTOBJECT_INNER_WORD] = {
		.txt = text_object_word,
	},
	[VIS_TEXTOBJECT_OUTER_WORD] = {
		.txt = text_object_word_outer,
	},
	[VIS_TEXTOBJECT_INNER_LONGWORD] = {
		.txt = text_object_longword,
	},
	[VIS_TEXTOBJECT_OUTER_LONGWORD] = {
		.txt = text_object_longword_outer,
	},
	[VIS_TEXTOBJECT_SENTENCE] = {
		.txt = text_object_sentence,
	},
	[VIS_TEXTOBJECT_PARAGRAPH] = {
		.txt = text_object_paragraph,
	},
	[VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET] = {
		.txt = text_object_square_bracket,
		.type = TEXTOBJECT_DELIMITED_OUTER,
	},
	[VIS_TEXTOBJECT_INNER_SQUARE_BRACKET] = {
		.txt = text_object_square_bracket,
		.type = TEXTOBJECT_DELIMITED_INNER,
	},
	[VIS_TEXTOBJECT_OUTER_CURLY_BRACKET] = {
		.txt = text_object_curly_bracket,
		.type = TEXTOBJECT_DELIMITED_OUTER,
	},
	[VIS_TEXTOBJECT_INNER_CURLY_BRACKET] = {
		.txt = text_object_curly_bracket,
		.type = TEXTOBJECT_DELIMITED_INNER,
	},
	[VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET] = {
		.txt = text_object_angle_bracket,
		.type = TEXTOBJECT_DELIMITED_OUTER,
	},
	[VIS_TEXTOBJECT_INNER_ANGLE_BRACKET] = {
		.txt = text_object_angle_bracket,
		.type = TEXTOBJECT_DELIMITED_INNER,
	},
	[VIS_TEXTOBJECT_OUTER_PARANTHESE] = {
		.txt = text_object_paranthese,
		.type = TEXTOBJECT_DELIMITED_OUTER,
	},
	[VIS_TEXTOBJECT_INNER_PARANTHESE] = {
		.txt = text_object_paranthese,
		.type = TEXTOBJECT_DELIMITED_INNER,
	},
	[VIS_TEXTOBJECT_OUTER_QUOTE] = {
		.txt = object_quote,
		.type = TEXTOBJECT_DELIMITED_OUTER,
	},
	[VIS_TEXTOBJECT_INNER_QUOTE] = {
		.txt = object_quote,
		.type = TEXTOBJECT_DELIMITED_INNER,
	},
	[VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE] = {
		.txt = object_single_quote,
		.type = TEXTOBJECT_DELIMITED_OUTER,
	},
	[VIS_TEXTOBJECT_INNER_SINGLE_QUOTE] = {
		.txt = object_single_quote,
		.type = TEXTOBJECT_DELIMITED_INNER,
	},
	[VIS_TEXTOBJECT_OUTER_BACKTICK] = {
		.txt = object_backtick,
		.type = TEXTOBJECT_DELIMITED_OUTER,
	},
	[VIS_TEXTOBJECT_INNER_BACKTICK] = {
		.txt = object_backtick,
		.type = TEXTOBJECT_DELIMITED_INNER,
	},
	[VIS_TEXTOBJECT_OUTER_ENTIRE] = {
		.txt = text_object_entire,
	},
	[VIS_TEXTOBJECT_INNER_ENTIRE] = {
		.txt = text_object_entire_inner,
        },
	[VIS_TEXTOBJECT_OUTER_LINE] = {
		.txt = text_object_line,
	},
	[VIS_TEXTOBJECT_INNER_LINE] = {
		.txt = text_object_line_inner,
	},
	[VIS_TEXTOBJECT_INDENTATION] = {
		.txt = text_object_indentation,
	},
	[VIS_TEXTOBJECT_SEARCH_FORWARD] = {
		.vis = search_forward,
		.type = TEXTOBJECT_NON_CONTIGUOUS|TEXTOBJECT_EXTEND_FORWARD,
	},
	[VIS_TEXTOBJECT_SEARCH_BACKWARD] = {
		.vis = search_backward,
		.type = TEXTOBJECT_NON_CONTIGUOUS|TEXTOBJECT_EXTEND_BACKWARD,
	},
};

