#define _GNU_SOURCE

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include <macros.h>
#include <ints.h>
#include <map.h>

#include <comp.h>


bool cmp_find(const map_t *pIN needle, const map_t *pIN haystack, usize *pOUT pos)
{
	u8* start;
	u8* substr = memmem(start = haystack->origin, haystack->len,
			needle->origin, needle->len);
	if(!substr) return false;
	debug_assert(substr >= start);
	*pos = (usize) (substr - start);
	return true;
}

int cmp_find_many(const map_t *pIN needle, usize nhaystacks, usize sizes[pOUT nhaystacks], ...)
{
	va_list v_haystacks;
	va_start(v_haystacks, sizes);

	const map_t* pINOUT haystack;
	register int rval=-1;
	for(usize i=0;i<nhaystacks;i++)
	{
		usize *pOUT size = sizes + i;
		haystack = va_arg(v_haystacks, const map_t* pIN);
		if(!haystack || !haystack->origin) FATAL("haystack %lu was null or its origin was null", i);
		if(!cmp_find(needle, haystack, size)) {
			rval = (int)i;
			break;
		}
	}

	va_end(v_haystacks);	
	return rval;
}

comp_multires_t* comp_match_all(map_t *pIN needle, usize nh, map_t hs[pIN nh], bool abort_on_fail)
{
	comp_multires_t* result = malloc(sizeof(comp_multires_t) + (sizeof(struct comp_single_result) * nh));
	debug_assert(result);

	result->all_matched = true;
	result->idx_first_failure = -1;
	result->num_results = nh;


	usize i;
	for(i =0;i<nh;i++)
	{
		usize start=0;
		bool matched = cmp_find(needle, hs+i, &start);
		struct comp_single_result res = {
			.mapping = hs+i,
			.matched = matched,
			.slice_start = start,
		};
		TRACE("Result for %lu (%p): Matched: %s, slice start: %lu", i, hs+i, matched ? "yes" : "no", start);
		result->aggr[i] = res;
		if(!res.matched) {
			WARN("Matching haystack %lu failed", i);
			result->all_matched = false;
			if(result->idx_first_failure < 0) {
				INFO("First match failure is %lu", i);
				result->idx_first_failure = (int)i;
			}
			if(abort_on_fail) {
				result->num_results = i;
				break;
			}
		}
	}
	INFO("Aggregate result for %lu / %lu haystack matches: Full passed: %s, first filaure: %d", 
							i, nh,
							result->all_matched ? "yes" : "no", 
							result->idx_first_failure);
	TRACE("Output result aggregate size: %lu (alloc: %p)", sizeof(comp_multires_t) + (sizeof(struct comp_single_result) * nh), result);
	return result;
}
