#ifndef STR_H
#define STR_H

#include <string.h>
#include <stddef.h>

#include "typ.h"
#include "arena.h"

typedef struct {
	char *s;
	isize n;
} Str;

typedef struct {
	Str head, tail;
} Cut;

#define S(s) (Str){s,sizeof(s)-1}

char *str_to_cstr(Str s, Arena *a);
Str str_from_cstr(const char *s);
int str_eql(Str a, Str b);
int str_starts(Str a, Str b);
int str_ends(Str a, Str b);
void str_catc(Str *a, char b, Arena *m);
Str str_skip(Str a, isize n);
int is_space(char c);
Str str_trim_left(Str a);
Str str_trim_right(Str a);
Str str_trim(Str a);
Cut str_cut(Str s, char c);
Str str_findc(Str s, char c);
Str str_find(Str haystack, Str needle);
int str_contains(Str a, Str b);
Str str_dup(Str a, Arena *m);
void str_cat(Str *a, Str b, Arena *m);
Str str_replace_end(Str s, Str a, Str b, Arena *m);

#ifdef STR_IMPL

/* conversions */

char *str_to_cstr(Str s, Arena *a) {
	char *r = new_arr(a, char, s.n + 1);
	memcpy(r, s.s, s.n);
	r[s.n] = 0;
	return r;
}

Str str_from_cstr(const char *s) {
	return (Str) { (char*)s, strlen(s) };
}

/* pure functions */

int str_eql(Str a, Str b) {
	return a.n == b.n && !memcmp(a.s, b.s, b.n);
}

int str_starts(Str a, Str b) {
	return a.n >= b.n && !memcmp(a.s, b.s, b.n);
}

int str_ends(Str a, Str b) {
	return a.n >= b.n && !memcmp(&a.s[a.n - b.n], b.s, b.n);
}

void str_catc(Str *a, char b, Arena *m) {
	a->s = resize(m, a->s, a->n, a->n + 1);
	a->s[a->n++] = b;
}

Str str_skip(Str a, isize n) {
	return (Str) { a.s + n, a.n - n };
}

int is_space(char c) {
	return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}

Str str_trim_left(Str a) {
	while (a.n > 0 && is_space(a.s[0])) a.s++, a.n--;
	return a;
}

Str str_trim_right(Str a) {
	while (a.n > 0 && is_space(a.s[a.n - 1])) a.n--;
	return a;
}

Str str_trim(Str a) {
	return str_trim_left(str_trim_right(a));
}

/* splitting, searching */

Cut str_cut(Str s, char c) {
	char *p = memchr(s.s, c, s.n);
	if (!p) {
		return (Cut) { s, { &s.s[s.n], 0 } };
	} else {
		return (Cut) {
			{ s.s, p - s.s },
			{ p + 1, &s.s[s.n] - (p + 1) }
		};
	}
}

Str str_findc(Str s, char c) {
	char *p = memchr(s.s, c, s.n);
	return p ? (Str) { p, s.n - (p - s.s) } : (Str) { &s.s[s.n], 0 };
}

Str str_find(Str haystack, Str needle) {
	if (needle.n < 1) return haystack;
	while (haystack.n > 0) {
		haystack = str_findc(haystack, needle.s[0]);
		if (str_starts(haystack, needle)) break;
	}
	return haystack;
}

int str_contains(Str a, Str b) {
	return str_find(a, b).n > 0;
}

/* allocating */

Str str_dup(Str a, Arena *m) {
	char *s = new_arr(m, char, a.n);
	memcpy(s, a.s, a.n);
	a.s = s;
	return a;
}

void str_cat(Str *a, Str b, Arena *m) {
	a->s = resize(m, a->s, a->n, a->n + b.n);
	memcpy(&a->s[a->n], b.s, b.n);
	a->n += b.n;
}

Str str_replace_end(Str s, Str a, Str b, Arena *m) {
	if (!str_ends(s, a)) return s;
	char *p = new_arr(m, char, s.n + b.n - a.n);
	memcpy(p, s.s, s.n - a.n);
	memcpy(p + s.n - a.n, b.s, b.n);
	return (Str) { p, s.n + b.n - a.n };
}

#endif
#endif