///                   _
/// __  ___   _ _ __ | |_ __ ___  __
/// \ \/ / | | | '_ \| __/ _` \ \/ /
///  >  <| |_| | | | | || (_| |>  <
/// /_/\_\\__, |_| |_|\__\__,_/_/\_\
///       |___/
///
/// Copyright (c) 1997 - Ognjen 'xolatile' Milan Robovic
///
/// xolatile@chud.cyou - xyntax - Tiny, unsafe and somewhat insane unity header for generic syntax definition.
///
/// This program is free software, free as in freedom and as in free beer, you can redistribute it and/or modify it under the terms of the GNU
/// General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version if you wish...
///
/// This program is distributed in the hope that it will be useful, but it is probably not, and without any warranty, without even the implied
/// warranty of merchantability or fitness for a particular purpose, because it is pointless. Please see the GNU (Geenoo) General Public License
/// for more details, if you dare, it is a lot of text that nobody wants to read...

typedef struct {
	natural     count;
	natural     limit;
	boolean   * enrange;
	boolean   * derange;
	character * * begin;
	character * * end;
	character   * escape;
	natural   * colour;
	natural   * effect;
} syntax_structure;

static syntax_structure * syntax_initialize (natural limit) {
	syntax_structure * syntax = allocate (sizeof (* syntax));

	syntax->limit = limit;

	if (limit != 0) {
		syntax->enrange = allocate (syntax->limit * sizeof (* syntax->enrange));
		syntax->derange = allocate (syntax->limit * sizeof (* syntax->derange));
		syntax->begin   = allocate (syntax->limit * sizeof (* syntax->begin));
		syntax->end     = allocate (syntax->limit * sizeof (* syntax->end));
		syntax->escape  = allocate (syntax->limit * sizeof (* syntax->escape));
		syntax->colour  = allocate (syntax->limit * sizeof (* syntax->colour));
		syntax->effect  = allocate (syntax->limit * sizeof (* syntax->effect));
	}

	return (syntax);
}

static syntax_structure * syntax_deinitialize (syntax_structure * syntax) {
	for (natural index = 0; index < syntax->count; ++index) {
		syntax->begin [index] = deallocate (syntax->begin [index]);
		syntax->end   [index] = deallocate (syntax->end   [index]);
	}

	syntax->enrange = deallocate (syntax->enrange);
	syntax->derange = deallocate (syntax->derange);
	syntax->begin   = deallocate (syntax->begin);
	syntax->end     = deallocate (syntax->end);
	syntax->escape  = deallocate (syntax->escape);
	syntax->colour  = deallocate (syntax->colour);
	syntax->effect  = deallocate (syntax->effect);

	return (deallocate (syntax));
}

static natural syntax_define (syntax_structure * syntax, boolean enrange, boolean derange, character * begin, character * end, character escape,
                           natural colour, natural effect) {
	++syntax->count;

	natural current = syntax->count - 1;

	fatal_failure (begin == null, "syntax_define: Begin string is null pointer.");
	fatal_failure (end   == null, "syntax_define: End string is null pointer.");

	fatal_failure (syntax->count >= syntax->limit, "syntax_define: Reached the hardcoded limit.");

	if (syntax->limit == 0) {
		syntax->enrange = reallocate (syntax->enrange, syntax->count * sizeof (* syntax->enrange));
		syntax->derange = reallocate (syntax->derange, syntax->count * sizeof (* syntax->derange));
		syntax->begin   = reallocate (syntax->begin,   syntax->count * sizeof (* syntax->begin));
		syntax->end     = reallocate (syntax->end,     syntax->count * sizeof (* syntax->end));
		syntax->escape  = reallocate (syntax->escape,  syntax->count * sizeof (* syntax->escape));
		syntax->colour  = reallocate (syntax->colour,  syntax->count * sizeof (* syntax->colour));
		syntax->effect  = reallocate (syntax->effect,  syntax->count * sizeof (* syntax->effect));
	}

	syntax->begin [current] = allocate ((string_length (begin) + 1) * sizeof (* * syntax->begin));
	syntax->end   [current] = allocate ((string_length (end)   + 1) * sizeof (* * syntax->end));

	syntax->enrange [current] = enrange;
	syntax->derange [current] = derange;
	syntax->escape  [current] = escape;
	syntax->colour  [current] = colour;
	syntax->effect  [current] = effect;

	string_copy (syntax->begin [current], begin);
	string_copy (syntax->end   [current], end);

	return (current);
}

static natural syntax_select (syntax_structure * syntax, character * string, natural * length) {
	natural offset = 0;
	natural subset = 0;
	natural select = 0;

	natural_64 begin_length = 0;
	natural_64 end_length   = 0;

	for (; select != syntax->count; ++select) {
		begin_length = string_length (syntax->begin [select]);

		if (! syntax->enrange [select]) {
			if (! syntax->derange [select]) {
				if (string_compare_limit (string, syntax->begin [select], begin_length)) {
					break;
				}
			} else {
				if ((string_compare_limit    (string, syntax->begin [select], begin_length))
				&&  (character_compare_array (string [offset + begin_length], syntax->end [select]))) {
					break;
				}
			}
		} else {
			for (subset = 0; subset != begin_length; ++subset) {
				if (string [offset] == syntax->begin [select] [subset]) {
					goto selected;
				}
			}
		}
	}

	selected:

	if (select >= syntax->count) {
		* length = 1;

		return (syntax->count);
	}

	end_length = string_length (syntax->end [select]);

	for (offset = 1; string [offset - 1] != character_null; ++offset) {
		if (string [offset] == syntax->escape [select]) {
			++offset;
			continue;
		}

		if (syntax->derange [select]) {
			subset = 0;
			if (end_length == 0) {
				break;
			} do {
				if (string [offset] == syntax->end [select] [subset]) {
					* length = offset;
					goto finished;
				}
			} while (++subset != end_length);
		} else {
			if (end_length != 0) {
				if (string_compare_limit (& string [offset], syntax->end [select], end_length)) {
					* length = offset + end_length;
					return (select);
				}
			} else {
				* length = 1;
				return (select);
			}
		}
	}

	finished:

	return (select);
}