#include "regex.h"

bool is_case_on = true;

static bool is_next_valid(const char * const s) {
	return *(s + 1);
}

static bool char_in_range(const char     start,
                          const char       end,
                          const char character) {
	if (start > end){
		return false;
	}

	for (char c = start; c != end; c++) {
		if (character == c) {
			return true;
		}
	}

	return false;
}

static bool is_word_separator(const char character) {
	return (( isascii(character))
	    &&  (!isalnum(character))
	    &&  ( character != '_'));
}

static bool magic(const char magic_char, const char to_enchant) {
	switch(magic_char){
		//	\i	identifier character (see 'isident' option)
		//	\I	like "\i", but excluding digits
		//	\k	keyword character (see 'iskeyword' option)
		//	\K	like "\k", but excluding digits
		//	\f	file name character (see 'isfname' option)
		//	\F	like "\f", but excluding digits
		//	\p	printable character (see 'isprint' option)
		//	\P	like "\p", but excluding digits
		case 's': {
			return ((to_enchant == ' ') || (to_enchant == '\t'));
		}
		case 'S': {
			return !((to_enchant == ' ') || (to_enchant == '\t'));
		}
		case 'd': {	// [0-9]
			return char_in_range('0', '9', to_enchant);
		};
		case 'D': {	// [^0-9]
			return !char_in_range('0', '9', to_enchant);
		};
		case 'x': { // [0-9A-Fa-f]
			return char_in_range('0', '9', to_enchant) || char_in_range('A', 'F', to_enchant) || char_in_range('a', 'f', to_enchant);
		};
		case 'X': { // [^0-9A-Fa-f]
			return !char_in_range('0', '9', to_enchant) && !char_in_range('A', 'F', to_enchant) && !char_in_range('a', 'f', to_enchant);
		};
		case 'o': { // [0-7]
			return char_in_range('0', '7', to_enchant);
		};
		case 'O': { // [^0-7]
			return !char_in_range('0', '7', to_enchant);
		};
		case 'w': { // [0-9A-Za-z_]
			return char_in_range('0', '9', to_enchant) || char_in_range('A', 'Z', to_enchant) || char_in_range('a', 'z', to_enchant) || (to_enchant == '_');
		};
		case 'W': { // [^0-9A-Za-z_]
			return !(char_in_range('0', '9', to_enchant) || char_in_range('A', 'Z', to_enchant) || char_in_range('a', 'z', to_enchant) || (to_enchant == '_'));
		};
		case 'h': { // [A-Za-z_]
			return char_in_range('A', 'Z', to_enchant) || char_in_range('a', 'z', to_enchant) || (to_enchant == '_');
		};
		case 'H': { // [^A-Za-z_]
			return !(char_in_range('A', 'Z', to_enchant) || char_in_range('a', 'z', to_enchant) || (to_enchant == '_'));
		};
		case 'a': { // [A-Za-z]
			return char_in_range('A', 'Z', to_enchant) || char_in_range('a', 'z', to_enchant);
		};
		case 'A': { // [A-Za-z]
			return !(char_in_range('A', 'Z', to_enchant) || char_in_range('a', 'z', to_enchant));
		};
		case 'l': { // [a-z]
			return char_in_range('a', 'z', to_enchant);
		};
		case 'L': { // [^a-z]
			return !(char_in_range('a', 'z', to_enchant));
		};
		case 'u': { // [A-Z]
			return char_in_range('A', 'Z', to_enchant);
		};
		case 'U': { // [^A-Z]
			return !(char_in_range('A', 'Z', to_enchant));
		};
	}

	return false;
}

int regex_match(const char * const pattern,
                   const char * const  string) {
	const char * pattern_pointer = pattern;
	const char * string_pointer = string;

	while (1488) {
		// End of one of the arguments
		if (!(*pattern_pointer)) {
			break;
		}
		if (!(*string_pointer)) {
			return false;
		}

		// Escape character
		if (*pattern_pointer == '\\') {
			if (!is_next_valid(pattern_pointer)) {
				return false;
			}

			switch(*(pattern_pointer + 1)){
				case 't': {
					if (*(string_pointer + 1) == '\t') {
						pattern_pointer += 2;
						string_pointer += 1;
					} else {
						return false;
					}
				} break;
				case 'r': {
					if (*(string_pointer + 1) == '\r') {
						pattern_pointer += 2;
						string_pointer += 1;
					} else {
						return false;
					}
				} break;
				case 'e': {
					if (*(string_pointer + 1) == '\033') {
						pattern_pointer += 2;
						string_pointer += 1;
					} else {
						return false;
					}
				} break;
				case 'b': {
					if (*(string_pointer + 1) == '\010') {
						pattern_pointer += 2;
						string_pointer += 1;
					} else {
						return false;
					}
				} break;
			}

			if (*(pattern_pointer + 1) == '\\') {
				if (*string_pointer == '\\') {
					pattern_pointer += 2;
					string_pointer += 1;
					continue;
				}
			}

			if (*(pattern_pointer + 1) == '<'
			&& is_word_separator(*string_pointer)) {
					pattern_pointer += 2;
					string_pointer += 1;
					continue;
			}

			if (*(pattern_pointer + 1) == '>') {
				if (is_word_separator(*(string_pointer + 1))) {
						pattern_pointer += 2;
						continue;
				}
				if (*(string_pointer + 1) == '\00') {
					break;
				}
			}

			if (magic(*(pattern_pointer + 1), *string_pointer)) {
				pattern_pointer += 2;
				string_pointer += 1;
				continue;
			}

			return false;
		}

		// Literal
		if (*pattern_pointer != *string_pointer) {
			return false;
		} else {
			++pattern_pointer;
			++string_pointer;
		}
	}

	return (string_pointer - string);
}