/*
 * Copyright 2025 Vsevolod Stakhov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef FSTRING_H
#define FSTRING_H

#include "config.h"
#include "mem_pool.h"
#include <unicode/uchar.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Fixed strings library
 * These strings are NOT null-terminated for speed
 */

typedef struct f_str_s {
	size_t len;
	size_t allocated;
	char str[];
} rspamd_fstring_t;

#define RSPAMD_FSTRING_DATA(s) ((s)->str)
#define RSPAMD_FSTRING_LEN(s) ((s)->len)
#define RSPAMD_FSTRING_LIT(lit) rspamd_fstring_new_init((lit), sizeof(lit) - 1)

typedef struct f_str_tok {
	size_t len;
	const char *begin;
} rspamd_ftok_t;

typedef struct f_str_unicode_tok {
	size_t len; /* in UChar32 */
	const UChar32 *begin;
} rspamd_ftok_unicode_t;

/**
 * Create new fixed length string
 */
rspamd_fstring_t *rspamd_fstring_new(void)
	G_GNUC_WARN_UNUSED_RESULT;

/**
 * Create new fixed length string with preallocated size
 */
rspamd_fstring_t *rspamd_fstring_sized_new(gsize initial_size)
	G_GNUC_WARN_UNUSED_RESULT;

/**
 * Create new fixed length string and initialize it with the initial data
 */
rspamd_fstring_t *rspamd_fstring_new_init(const char *init, gsize len)
	G_GNUC_WARN_UNUSED_RESULT;

/**
 * Assign new value to fixed string
 */
rspamd_fstring_t *rspamd_fstring_assign(rspamd_fstring_t *str,
										const char *init, gsize len) G_GNUC_WARN_UNUSED_RESULT;

/**
 * Free fixed length string
 */
void rspamd_fstring_free(rspamd_fstring_t *str);

/**
 * Append data to a fixed length string
 */
rspamd_fstring_t *rspamd_fstring_append(rspamd_fstring_t *str,
										const char *in, gsize len) G_GNUC_WARN_UNUSED_RESULT;

/**
 * Append `len` repeated chars `c` to string `str`
 */
rspamd_fstring_t *rspamd_fstring_append_chars(rspamd_fstring_t *str,
											  char c, gsize len) G_GNUC_WARN_UNUSED_RESULT;

/**
 * Erase `len` characters at position `pos`
 */
void rspamd_fstring_erase(rspamd_fstring_t *str, gsize pos, gsize len);

#define rspamd_fstring_clear(s) rspamd_fstring_erase(s, 0, s->len)

/**
 * Convert fixed string to a zero terminated string. This string must be
 * freed by a caller
 */
char *rspamd_fstring_cstr(const rspamd_fstring_t *str)
	G_GNUC_WARN_UNUSED_RESULT;

/**
 * Convert fixed string usign ftok_t to a zero terminated string. This string must be
 * freed by a caller
 */
char *rspamd_ftok_cstr(const rspamd_ftok_t *str)
	G_GNUC_WARN_UNUSED_RESULT;

/*
 * Return fast hash value for fixed string converted to lowercase
 */
uint32_t rspamd_fstrhash_lc(const rspamd_ftok_t *str, gboolean is_utf);

/**
 * Return true if two strings are equal
 */
gboolean rspamd_fstring_equal(const rspamd_fstring_t *s1,
							  const rspamd_fstring_t *s2);

/**
 * Compare two fixed strings ignoring case
 */
int rspamd_fstring_casecmp(const rspamd_fstring_t *s1,
						   const rspamd_fstring_t *s2);

/**
 * Compare two fixed strings
 */
int rspamd_fstring_cmp(const rspamd_fstring_t *s1,
					   const rspamd_fstring_t *s2);

/**
 * Compare two fixed tokens ignoring case
 */
int rspamd_ftok_casecmp(const rspamd_ftok_t *s1,
						const rspamd_ftok_t *s2);

/**
 * Compare two fixed tokens
 */
int rspamd_ftok_cmp(const rspamd_ftok_t *s1,
					const rspamd_ftok_t *s2);

/**
 * Returns true if `s1` starts with `s2`
 * @param s1
 * @param s2
 * @return
 */
gboolean rspamd_ftok_starts_with(const rspamd_ftok_t *s1,
								 const rspamd_ftok_t *s2);

/**
 * Return TRUE if ftok is equal to specified C string
 */
gboolean rspamd_ftok_cstr_equal(const rspamd_ftok_t *s,
								const char *pat, gboolean icase);

/**
 * Free fstring_t that is mapped to ftok_t
 *
 * | len | allocated | <data> -- fstring_t
 *                     <begin> -- tok
 *
 * tok is expected to be allocated with g_malloc
 */
void rspamd_fstring_mapped_ftok_free(gpointer p);

/**
 * Map token to a specified string. Token must be freed using g_free
 */
rspamd_ftok_t *rspamd_ftok_map(const rspamd_fstring_t *s);

/**
 * Suggest suitable size to grow fstring
 * @param len
 * @param allocated
 * @param needed_len
 * @return
 */
gsize rspamd_fstring_suggest_size(gsize len, gsize allocated, gsize needed_len);

/**
 * Grow the specified fixed string
 * @param str
 * @param needed_len
 * @return
 */
rspamd_fstring_t *rspamd_fstring_grow(rspamd_fstring_t *str,
									  gsize needed_len) G_GNUC_WARN_UNUSED_RESULT;

/**
 * Copies ftok to zero terminated string (must be freed using g_free)
 * @param src
 * @return
 */
char *rspamd_ftokdup(const rspamd_ftok_t *src) G_GNUC_WARN_UNUSED_RESULT;

/**
 * Copies fstring to zero terminated string (must be freed using g_free)
 * @param src
 * @return
 */
char *rspamd_fstringdup(const rspamd_fstring_t *src) G_GNUC_WARN_UNUSED_RESULT;

#define RSPAMD_FTOK_ASSIGN(t, lit)  \
	do {                            \
		(t)->begin = (lit);         \
		(t)->len = sizeof(lit) - 1; \
	} while (0)
#define RSPAMD_FTOK_FROM_STR(t, str)           \
	do {                                       \
		if (G_LIKELY(str)) {                   \
			(t)->begin = (const char *) (str); \
			(t)->len = strlen(str);            \
		}                                      \
		else {                                 \
			(t)->begin = NULL;                 \
			(t)->len = 0;                      \
		}                                      \
	} while (0)

#ifdef __cplusplus
}
#endif
#endif
