/* See LICENSE file for copyright and license details. */
#ifndef UTIL_H
#define UTIL_H

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

#include "../gen/types.h"
#include "../grapheme.h"

#undef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#undef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#undef LEN
#define LEN(x) (sizeof(x) / sizeof(*(x)))

#undef likely
#undef unlikely
#ifdef __has_builtin
#if __has_builtin(__builtin_expect)
#define likely(expr)   __builtin_expect(!!(expr), 1)
#define unlikely(expr) __builtin_expect(!!(expr), 0)
#else
#define likely(expr)   (expr)
#define unlikely(expr) (expr)
#endif
#else
#define likely(expr)   (expr)
#define unlikely(expr) (expr)
#endif

/*
 * Herodotus, the ancient greek historian and geographer,
 * was criticized for including legends and other fantastic
 * accounts into his works, among others by his contemporary
 * Thucydides.
 *
 * The Herodotus readers and writers are tailored towards the needs
 * of the library interface, doing all the dirty work behind the
 * scenes. While the reader is relatively faithful in his accounts,
 * the Herodotus writer will never fail and always claim to write the
 * data. Internally, it only writes as much as it can, and will simply
 * keep account of the rest. This way, we can properly signal truncation.
 *
 * In this sense, explaining the naming, the writer is always a bit
 * inaccurate in his accounts.
 *
 */
enum herodotus_status {
	HERODOTUS_STATUS_SUCCESS,
	HERODOTUS_STATUS_END_OF_BUFFER,
	HERODOTUS_STATUS_SOFT_LIMIT_REACHED,
};

enum herodotus_type {
	HERODOTUS_TYPE_CODEPOINT,
	HERODOTUS_TYPE_UTF8,
};

typedef struct herodotus_reader {
	enum herodotus_type type;
	const void *src;
	size_t srclen;
	size_t off;
	bool terminated_by_null;
	size_t soft_limit[10];
} HERODOTUS_READER;

typedef struct herodotus_writer {
	enum herodotus_type type;
	void *dest;
	size_t destlen;
	size_t off;
	size_t first_unwritable_offset;
} HERODOTUS_WRITER;

struct proper {
	/*
	 * prev_prop[1] prev_prop[0] | next_prop[0] next_prop[1]
	 */
	struct {
		uint_least8_t prev_prop[2];
		uint_least8_t next_prop[2];
	} raw, skip;

	HERODOTUS_READER mid_reader, raw_reader, skip_reader;
	void *state;
	uint_least8_t no_prop;
	uint_least8_t (*get_break_prop)(uint_least32_t);
	bool (*is_skippable_prop)(uint_least8_t);
	void (*skip_shift_callback)(uint_least8_t, void *);
};

void herodotus_reader_init(HERODOTUS_READER *, enum herodotus_type,
                           const void *, size_t);
void herodotus_reader_copy(const HERODOTUS_READER *, HERODOTUS_READER *);
void herodotus_reader_push_advance_limit(HERODOTUS_READER *, size_t);
void herodotus_reader_pop_limit(HERODOTUS_READER *);
size_t herodotus_reader_number_read(const HERODOTUS_READER *);
size_t herodotus_reader_next_word_break(const HERODOTUS_READER *);
size_t herodotus_reader_next_codepoint_break(const HERODOTUS_READER *);
enum herodotus_status herodotus_read_codepoint(HERODOTUS_READER *, bool,
                                               uint_least32_t *);

void herodotus_writer_init(HERODOTUS_WRITER *, enum herodotus_type, void *,
                           size_t);
void herodotus_writer_nul_terminate(HERODOTUS_WRITER *);
size_t herodotus_writer_number_written(const HERODOTUS_WRITER *);
void herodotus_write_codepoint(HERODOTUS_WRITER *, uint_least32_t);

void proper_init(const HERODOTUS_READER *, void *, uint_least8_t,
                 uint_least8_t (*get_break_prop)(uint_least32_t),
                 bool (*is_skippable_prop)(uint_least8_t),
                 void (*skip_shift_callback)(uint_least8_t, void *),
                 struct proper *);
int proper_advance(struct proper *);

#endif /* UTIL_H */
