/*
 * TSM - Main internal header
 *
 * Copyright (c) 2018 Aetf <aetf@unlimitedcodeworks.xyz>
 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef TSM_LIBTSM_INT_H
#define TSM_LIBTSM_INT_H

#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "libtsm.h"
#include "shl_dlist.h"
#include "shl-llog.h"

#define SHL_EXPORT __attribute__((visibility("default")))

/* max combined-symbol length */
#define TSM_UCS4_MAXLEN 10

/* symbols */

struct tsm_symbol_table;

extern const tsm_symbol_t tsm_symbol_default;

int tsm_symbol_table_new(struct tsm_symbol_table **out);
void tsm_symbol_table_ref(struct tsm_symbol_table *tbl);
void tsm_symbol_table_unref(struct tsm_symbol_table *tbl);

tsm_symbol_t tsm_symbol_make(uint32_t ucs4);
tsm_symbol_t tsm_symbol_append(struct tsm_symbol_table *tbl,
			       tsm_symbol_t sym, uint32_t ucs4);
const uint32_t *tsm_symbol_get(struct tsm_symbol_table *tbl,
			       tsm_symbol_t *sym, size_t *size);
unsigned int tsm_symbol_get_width(struct tsm_symbol_table *tbl,
				  tsm_symbol_t sym);

/* utf8 state machine */

struct tsm_utf8_mach;

enum tsm_utf8_mach_state {
	TSM_UTF8_START,
	TSM_UTF8_ACCEPT,
	TSM_UTF8_REJECT,
	TSM_UTF8_EXPECT1,
	TSM_UTF8_EXPECT2,
	TSM_UTF8_EXPECT3,
};

int tsm_utf8_mach_new(struct tsm_utf8_mach **out);
void tsm_utf8_mach_free(struct tsm_utf8_mach *mach);

int tsm_utf8_mach_feed(struct tsm_utf8_mach *mach, char c);
uint32_t tsm_utf8_mach_get(struct tsm_utf8_mach *mach);
void tsm_utf8_mach_reset(struct tsm_utf8_mach *mach);

/* TSM screen */

struct cell {
	tsm_symbol_t ch;		/* stored character */
	unsigned int width;		/* character width */
	struct tsm_screen_attr attr;	/* cell attributes */
	tsm_age_t age;			/* age of the single cell */
};

struct line {
	struct shl_dlist list;		/* list node, next/prev are NULL if not in sb */
	unsigned int size;		/* real width */
	struct cell *cells;		/* actuall cells */
	uint64_t sb_id;			/* sb ID, 0 if not in sb */
	tsm_age_t age;			/* age of the whole line */
};

struct selection_pos {
	unsigned int x;			/* x offset from the start of the line */
	struct line *line;		/* line the selection is on */
};

struct tsm_scrollback {
	/* scroll-back buffer */
	unsigned int count;		/* number of lines in sb */
	struct shl_dlist list;	/* list of lines in sb */
	unsigned int max;		/* max-limit of lines in sb */
	struct line *pos;		/* current position in sb or NULL */
	unsigned int pos_num;	/* current numeric position in sb */
	uint64_t last_id;		/* last id given to sb-line */
};

struct tsm_screen {
	size_t ref;
	llog_submit_t llog;
	void *llog_data;
	unsigned int opts;
	unsigned int flags;
	struct tsm_symbol_table *sym_table;

	/* default attributes for new cells */
	struct tsm_screen_attr def_attr;

	/* save default attributes of main screen here when we switch to alt screen
	 * on resize of the alt screen we need to init the new cells of the main
	 * screen with these attributes and not the ones of the alt screen */
	struct tsm_screen_attr def_attr_main;

	/* ageing */
	tsm_age_t age_cnt;		/* current age counter */
	unsigned int age_reset : 1;	/* age-overflow flag */

	/* current buffer */
	unsigned int size_x;		/* width of screen */
	unsigned int size_y;		/* height of screen */
	unsigned int margin_top;	/* top-margin index */
	unsigned int margin_bottom;	/* bottom-margin index */
	unsigned int line_num;		/* real number of allocated lines */
	struct line **lines;		/* active lines; copy of main/alt */
	struct line **main_lines;	/* real main lines */
	struct line **alt_lines;	/* real alternative lines */
	tsm_age_t age;			/* whole screen age */

	struct tsm_scrollback sb;

	/* cursor: positions are always in-bound, but cursor_x might be
	 * bigger than size_x if new-line is pending */
	unsigned int cursor_x;		/* current cursor x-pos */
	unsigned int cursor_y;		/* current cursor y-pos */

	/* tab ruler */
	bool *tab_ruler;		/* tab-flag for all cells of one row */

	/* selection */
	bool sel_active;
	struct selection_pos sel_start;
	struct selection_pos sel_end;
};

void screen_cell_init(struct tsm_screen *con, struct cell *cell);

void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts);
void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts);
unsigned int tsm_screen_get_opts(struct tsm_screen *scr);
void tsm_screen_repeat_char(struct tsm_screen *con, unsigned int num);

static inline void screen_inc_age(struct tsm_screen *con)
{
	if (!++con->age_cnt) {
		con->age_reset = 1;
		++con->age_cnt;
	}
}

static inline bool is_in_scrollback(struct selection_pos *sel) {
	return (sel->line && sel->line->sb_id);
}

/* available character sets */

typedef tsm_symbol_t tsm_vte_charset[96];

extern tsm_vte_charset tsm_vte_unicode_lower;
extern tsm_vte_charset tsm_vte_unicode_upper;
extern tsm_vte_charset tsm_vte_dec_supplemental_graphics;
extern tsm_vte_charset tsm_vte_dec_special_graphics;

#endif /* TSM_LIBTSM_INT_H */
