/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   libtf.h                                            :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: mcolonna <marvin@42.fr>                    +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2023/12/18 11:07:59 by mcolonna          #+#    #+#             */
/*   Updated: 2024/03/11 12:12:03 by mcolonna         ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#ifndef LIBTF_H
# define LIBTF_H

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

///// STR (part1) /////
/**
 * t_string variables are char* which ends with '\0'.
 */
typedef char		*t_string;

/**
 * Same but the chars are constant.
 */
typedef const char	*t_const_string;

///// ERR /////
// Functions who can raise an error must accept a t_err function
// and call it if an error occurs.

/**
 * Function called when there's an error.
 * The argument identifies what type of error is it (can be NULL).
 * In this library, the possible arguments are:
 * -	"alloc error"	Error when allocating memory.
 * -	"bad value"		The value given to the function is bad.
 * -	"errno"			The error is defined in errno.
 * -	"empty list"	The list is empty and it shouldn't be.
 */
typedef void		(t_err)(t_const_string);

/**
 * Keep the error in memory to get it later with get_error().
 */
void				err_remember(t_const_string msg);

/**
 * If err_remember was called before, return the message of the error
 * then drop it. Return NULL if there is no error to get.
 */
t_const_string		err_get(void);

///// MEM /////
// Awesome malloc manager. When you alloc an address, you have to assign
// a t_memclass (mc) to it. Then, you can free every address assigned to
// a mc using mem_freeall(), or you can free the addresses one by one
// using mem_free() (you still have to use mem_freeall() to free the mc).

/**
 * Represent a class of allocs.
 * Must be created by mem_newclass() and freed by mem_freeall()
 * Don't use after being freed.
 */
typedef void		*t_memclass;

/**
 * Create a t_memclass.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_memclass			mem_newclass(t_err *err);

/**
 * Create a t_memclass and add it to parent.
 * It means if we freeall(parent), this mc is freeall too.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_memclass			mem_subclass(t_err *err, t_memclass parent);

/**
 * Alloc memory of the class 'mc' and of the size 'size'.
 * If mc is NULL, do nothing.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
void				*mem_alloc(t_err *err, t_memclass mc, size_t size);

/**
 * Free all the memory of the specified class.
 * 'mc' becomes unusable.
 */
void				mem_freeall(t_memclass mc);

/**
 * Free a pointer. If pointer is NULL, do nothing.
 * /!\ Must use it only with address alloced with mem_alloc!!
 */
void				mem_free(void *pointer);

///// STR (part2) /////

/**
 * Return the length of the string.
 */
int					str_len(t_const_string str);

/**
 * Clone the string.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_string			str_dup(t_err *err, t_memclass mc, t_const_string str);

/**
 * Check if the char is in the given charset.
 * Return always false if c is '\0'.
 */
bool				char_isin(char c, t_const_string charset);

/**
 * Check if the char is a digit ([0-9]).
 */
bool				char_isdigit(char c);

/**
 * Check if the char is an uppercase letter ([A-Z]).
 */
bool				char_isupper(char c);

/**
 * Check if the char is a lowercase letter ([a-z]).
 */
bool				char_islower(char c);

/**
 * Check if the char is a letter ([A-Za-z]).
 */
bool				char_isalpha(char c);
/**
 * Check if the char is a letter or a digit ([A-Za-z0-9]).
 */
bool				char_isalphanum(char c);

/**
 * Allows reading a string char by char.
 */
typedef struct s_stream
{
	char		(*_read)(const void **);
	char		(*_pop)(const void **);
	const void	*_data;
}			t_stream;

/**
 * Create a stream from a string.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_stream			*streamstr_alloc(
						t_err *err, t_memclass mc, t_const_string str);
/**
 * Set 'stream' to a new stream using 'str'.
 */
void				streamstr_init(t_stream *stream, t_const_string str);

/**
 * Return the current char of the stream, or \0 if it's the end.
 */
char				stream_read(t_stream *stream);

/**
 * Return the current char of the stream, or \0 if it's the end.
 * Go to the next char if it isn't the end.
 */
char				stream_pop(t_stream *stream);

/**
 * Skip specified chars in stream.
 * For example if the stream is at "21012321" and charset is "012",
 * it will skip the first '0', '1' and '2's to be at "321".
 */
void				stream_skip(t_stream *stream, t_const_string charset);

/**
 * Read an unsigned int in decimal, until a non-digit or the end of the stream.
 * If there is no digit, or if it overflows the length of a unsigned int, err
 * is called with "bad value" and 0 is returned.
 * Possible errors: "bad value"
 * Return 0 on error.
 */
unsigned int		stream_readuint(t_err *err, t_stream *stream);

/**
 * Read a signed int in decimal, until a non-digit or the end of the stream.
 * If there is no digit, '+' or '-', or if it overflows the length of a signed
 * int, err is called with "bad value" and -1 is returned.
 * Possible errors: "bad value"
 * Return 0 on error.
 */
int					stream_readint(t_err *err, t_stream *stream);

/**
 * Cast a string to an unsigned int in decimal.
 * The string must match /[0-9]+/, and the result must not overflow the limits
 * of a unsigned int. If it isn't respected, err is called with "bad value"
 * and 0 is returned.
 * Possible errors: "bad value"
 * Return 0 on error.
 */
unsigned int		str_touint(t_err *err, t_const_string str);

/**
 * Cast a string to a signed int in decimal.
 * The string must match /[-+]?[0-9]+/, and the result must not overflow the
 * limits of an int. If it isn't respected, err is called with "bad value" and
 * -1 is returned.
 * Possible errors: "bad value"
 * Return -1 on error.
 */
int					str_toint(t_err *err, t_const_string str);

/**
 * Split the given string by the given separators in charset.
 * Return a list of strings ended by NULL, no empty string.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_string			*str_split(
						t_err *err, t_memclass mc, t_const_string str,
						t_const_string charset);

/**
 * Return a mem_alloc'd concatenation of the two strings.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_string			str_join(t_err *err, t_memclass mc, t_const_string str1,
						t_const_string str2);

/**
 * Write an int as an str in decimal.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_string			str_inttostr(t_err *err, t_memclass mc, int n);

/**
 * Write an unsigned int as an str in decimal.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
t_string			str_uinttostr(t_err *err, t_memclass mc, unsigned int n);

/**
 * Return true if the strings are the same.
 */
bool				str_eq(t_const_string str1, t_const_string str2);

///// IO /////

/**
 * Write a string on fd.
 * Possible errors: "errno"
 */
void				print_str(t_err *err, int fd, t_const_string str);

/**
 * Write a string on fd and finish it with a \n.
 * Possible errors: "errno"
 */
void				print_line(t_err *err, int fd, t_const_string str);

/**
 * Conversions: %c %s %p %d %i %u %x %X %%
 * Return -1 on error.
 */
int					tf_printf(t_const_string format, ...);

/**
 * Return the next line of the 'fd', including the '\n' if it exists.
 * 'fd' must be between 0 and 4095 included.
 * Possible error: "alloc error", "bad value", "errno"
 * Return NULL if error, BUT it's not the only case it could return NULL!
 * To avoid leaks and problems, must be called until it returns NULL.
 */
t_const_string		read_line(t_err err, t_memclass mc, int fd);

///// LIST /////

/**
 * Represents an element of a t_list.
 * - value: mem_alloc'd pointer to the value of the element.
 * - next: next element in the list, or NULL if it's the end of the list.
 * - previous: previous element in the list, or NULL if it's the beginning of the
 *     list.
 */
typedef struct s_list_element
{
	void					*value;
	struct s_list_element	*next;
	struct s_list_element	*previous;
}							t_list_element;

/**
 * Represents a list.
 * - first: first element of the list, or NULL if the list is empty.
 * - last: last element of the list, or NULL if the list is empty.
 * - size: number of elements in the list.
 * - mc: memclass of the elements and their values.
 * [!] All those fields must stay coherent.
 */
typedef struct s_list
{
	t_list_element	*first;
	t_list_element	*last;
	int				size;
	t_memclass		mc;
}			t_list;

/**
 * Create an empty list.
 * The mc is saved to be used y list_addelement. You must do mem_freeall(mc)
 * after finishing using the list.
 */
t_list				list_createempty(t_memclass mc);

/**
 * Add an element to the start of the list.
 * The element is alloc'd with the mc of the list.
 * Possible errors: "alloc error"
 */
void				list_revadd(t_err *err, t_list *list, void *value);

/**
 * Add an element to the end of the list.
 * The element is alloc'd with the mc of the list.
 * err is called with "alloc error" if the alloc doesn't work.
 * Possible errors: "alloc error"
 */
void				list_add(t_err *err, t_list *list, void *value);

/**
 * Get the value at the specified index.
 * Possible errors: "alloc error"
 * Return NULL on error.
 */
void				*list_get(t_err *err, const t_list *list, int index);

/**
 * Return the last value from the list and remove it.
 * Possible errors: "empty list"
 * Return NULL on error.
 */
void				*list_revpop(t_err *err, t_list *list);

/**
 * Return the first value from the list and remove it.
 * Possible errors: "empty list"
 * Return NULL on error.
 */
void				*list_pop(t_err *err, t_list *list);

/**
 * Return the size of the list.
 */
int					list_getsize(const t_list *list);

/**
 * Move the first element to the end of the list.
 * Does nothing if empty.
 */
void				list_rotate(t_list *list);

/**
 * Move the last element to the beginning of the list.
 * Does nothing if empty.
 */
void				list_revrotate(t_list *list);

///// DATA /////

/**
 * Copy 'size' bytes of content from src to dest.
 * The ranges CAN overlap.
 */
void				data_cpy(void *dest, const void *src, size_t size);

#endif
