#ifndef R_STRS_H
#define R_STRS_H

#include <r_types.h>
#include <string.h>
#include <ctype.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct r_strs_t {
	const char *a;
	const char *b;
} RStrs;

#define R_STRS_LIT(s) r_strs_from_len ((s), sizeof ("" s) - 1)

/* Constructors */
static inline RStrs r_strs_new(const char *a, const char *b) {
	RStrs r = { a, b };
	return r;
}

static inline RStrs r_strs_from_len(const char *s, size_t n) {
	RStrs r = { s, s? s + n: NULL };
	return r;
}

static inline RStrs r_strs_from(const char *s) {
	RStrs r = { s, s? s + strlen (s): NULL };
	return r;
}

/* Queries */
static inline size_t r_strs_len(RStrs s) {
	return (s.a && s.b > s.a)? (size_t)(s.b - s.a): 0;
}

static inline bool r_strs_empty(RStrs s) {
	return !s.a || s.b <= s.a;
}

static inline char r_strs_at(RStrs s, size_t i) {
	return (i < r_strs_len (s))? s.a[i]: 0;
}

/* Subslicing */
static inline RStrs r_strs_sub(RStrs s, size_t from, size_t to) {
	const size_t l = r_strs_len (s);
	if (from > l) {
		from = l;
	}
	if (to > l) {
		to = l;
	}
	if (to < from) {
		to = from;
	}
	RStrs r = { s.a + from, s.a + to };
	return r;
}

/* Navigation. Mutators require a valid `s`; passing NULL is a caller bug. */
static inline bool r_strs_advance(RStrs *s, size_t n) {
	if (n > r_strs_len (*s)) {
		s->a = s->b;
		return false;
	}
	s->a += n;
	return true;
}

/* Whitespace trimming */
static inline void r_strs_trim(RStrs *s) {
	while (s->a < s->b && isspace ((unsigned char)*s->a)) {
		s->a++;
	}
	while (s->b > s->a && isspace ((unsigned char)s->b[-1])) {
		s->b--;
	}
}

/* Equality / prefix — canonical slice versions */
static inline bool r_strs_equals(RStrs a, RStrs b) {
	const size_t la = r_strs_len (a);
	return la == r_strs_len (b) && (la == 0 || !memcmp (a.a, b.a, la));
}

static inline bool r_strs_startswith_strs(RStrs s, RStrs prefix) {
	const size_t n = r_strs_len (prefix);
	return n == 0 || (r_strs_len (s) >= n && !memcmp (s.a, prefix.a, n));
}

/* C-string convenience wrappers — strlen folds to a constant for literals */
static inline bool r_strs_equals_str(RStrs s, const char *str) {
	return r_strs_equals (s, r_strs_from (str));
}

static inline bool r_strs_startswith(RStrs s, const char *prefix) {
	return r_strs_startswith_strs (s, r_strs_from (prefix));
}

/* Search (out of line primitives) */
R_API const char *r_strs_find_strs(RStrs s, RStrs needle);
R_API const char *r_strs_findc(RStrs s, char c);
R_API const char *r_strs_find_any(RStrs s, const char *set);

/* Char-set skip, tokenization (out of line) */
R_API void r_strs_skip_chars(RStrs *s, const char *set);
R_API void r_strs_trim_chars(RStrs *s, const char *set);
R_API RStrs r_strs_take_ident(RStrs *s);
R_API bool r_strs_next_token(RStrs *s, const char *seps, RStrs *out);

/* Splitting — inline wrappers around the search primitives. Both `head` and
 * `tail` must be non-NULL; pass a throwaway slot if a side is unwanted. */
static inline bool r_strs_split(RStrs s, char sep, RStrs *head, RStrs *tail) {
	const char *p = r_strs_findc (s, sep);
	if (!p) {
		*head = s;
		tail->a = tail->b = s.b;
		return false;
	}
	head->a = s.a; head->b = p;
	tail->a = p + 1; tail->b = s.b;
	return true;
}

static inline bool r_strs_split_any(RStrs s, const char *seps, RStrs *head, RStrs *tail) {
	const char *p = r_strs_find_any (s, seps);
	if (!p) {
		*head = s;
		tail->a = tail->b = s.b;
		return false;
	}
	head->a = s.a; head->b = p;
	tail->a = p + 1; tail->b = s.b;
	return true;
}

static inline bool r_strs_split_strs(RStrs s, RStrs sep, RStrs *head, RStrs *tail) {
	const size_t n = r_strs_len (sep);
	const char *p = n? r_strs_find_strs (s, sep): NULL;
	if (!p) {
		*head = s;
		tail->a = tail->b = s.b;
		return false;
	}
	head->a = s.a; head->b = p;
	tail->a = p + n; tail->b = s.b;
	return true;
}

/* Conversion (out of line) */
R_API char *r_strs_tostring(RStrs s);
R_API st64 r_strs_tosnum(RStrs s, bool *ok);

/* Parse a slice as an unsigned integer, respecting slice bounds (no strlen,
 * no malloc). Recognises plain integer literals with optional C integer
 * suffixes; for richer syntax (signed, 0b, 0o, 0t, expressions, …) call
 * r_num_get.
 *
 *   base = 0   auto-detect: "0x" / "0X" prefix → hex, else decimal
 *   base = 10  decimal only (digits)
 *   base = 16  hex (optional "0x" / "0X" prefix, then hex digits)
 *
 * If error is non-NULL, *error is set to true on empty/malformed input and
 * false on success. On error the return value is 0. */
R_API ut64 r_strs_tonum(RStrs s, int base, bool *error);

/* Write `n` as a "0x…" hex string into `buf` (capacity `cap`) and return the
 * resulting slice. `cap` must be at least 19 (worst case: "0x" + 16 hex + NUL).
 * On overflow returns an empty RStrs. Buf is always NUL-terminated on success. */
R_API RStrs r_strs_u64hex(char *buf, size_t cap, ut64 n);

#ifdef __cplusplus
}
#endif

#endif
