#ifndef STR_H
#define STR_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>


struct str {
	const char *data;
	size_t size;
};

const struct str STR_EMPTY = {"", 0};

struct str_tokenizer {
	struct str str;
	size_t cur;
	char c;
};

struct str str_from_cstr(const char *cstr, size_t cstr_size);
struct str str_rstrip(struct str str);
struct str str_lstrip(struct str str);
struct str str_strip(struct str str);

struct str_tokenizer str_tokenize(struct str str, char c);
struct str str_tokenizer_next(struct str_tokenizer *st);

struct str str_slice(struct str str, size_t from, size_t to);

bool str_eq_cstr(struct str str, const char *cstr, size_t cstr_size);

#if defined(IMP) || defined(IMP_STR)

#include <ctype.h>

struct str 
str_from_cstr(const char *cstr, size_t cstr_size)
{
	struct str str;
	str.data = cstr;
	str.size = cstr_size;
	return str;
}

struct str
str_rstrip(struct str str)
{
	while ( isspace(str.data[str.size-1]) ) {
		--str.size;
	}
	return str;
}

struct str 
str_lstrip(struct str str)
{
	while ( isspace(*str.data) ) {
		++str.data;
		--str.size;
	}
	return str;
}

struct str 
str_strip(struct str str)
{
	return str_lstrip(str_rstrip(str));
}

struct str_tokenizer 
str_tokenize(struct str str, char c)
{
	struct str_tokenizer st = {0};
	st.str = str;
	st.c = c;
	return st;
}

struct str 
str_tokenizer_next(struct str_tokenizer *st)
{
	struct str str;

	if ( st == NULL ) {
		goto ret_err;
	}

	if ( st->cur >= st->str.size ) {
		goto ret_done;
	}

	if ( st->str.data[st->cur] == '\0' ) {
		goto ret_done;
	}

	str = st->str;
	str.data += st->cur;
	str.size = 0;

	while ( str.data[str.size] != st->c 
		&& st->cur < st->str.size ) {
		++str.size;
		++st->cur;
	}
	++st->cur;

	if ( str.size == 0 ) {
		goto ret_empty;
	}

	return str;

ret_empty:
	str.data = "";
	str.size = 0;
	return str;

ret_done:
	str.data = "";
	str.size = (size_t) -1;
	return str;

ret_err:
	str.data = "";
	str.size = (size_t) -2;
	return str;
}

struct str 
str_slice(struct str str, size_t from, size_t to)
{
	if ( from > str.size ) {
		goto ret_err;
	}
	if ( from > to ) {
		goto ret_err;
	}

	to = ( to > str.size ) * str.size \
	     + ( to <= str.size ) * to;
	
	str.data += from;
	str.size = to - from;

	return str;

ret_err:
	str.data = "";
	str.size = (size_t) -2;
	return str;
}

bool 
str_eq_cstr(struct str str, const char *cstr, size_t cstr_size)
{
	size_t i = 0;
	if ( str.size != cstr_size ) {
		return false;
	}

	for ( i = 0; i < str.size; ++i ) {
		if ( str.data[i] != cstr[i] ) {
			return false;
		}
	}

	return true;
}

#endif /* defined(IMP) || defined(IMP_STR) */

#endif /* STR_H */
