/// \file
///
/// this file is included by (almost) all of ted's source files.
/// it may change arbitrarily so plugin authors should not include it!

#ifndef TED_INTERNAL_H_
#define TED_INTERNAL_H_

#include "ted.h"
#include "lsp.h"
#include "os.h"
#include "unicode.h"
#include "ds.h"
#include "sdl-inc.h"
#include "lib/glcorearb.h"

#if PROFILE
#define PROFILE_TIME(var) double var = time_get_seconds();
#else
/// get current time for profiling
#define PROFILE_TIME(var)
#endif


/// Minimum text size
#define TEXT_SIZE_MIN 6
/// Maximum text size
#define TEXT_SIZE_MAX 70
/// max number of LSPs running at once
#define TED_LSP_MAX 200
/// max number of macros
#define TED_MACRO_MAX 256
/// max number of nodes
#define TED_NODE_MAX 256
/// max number of buffers
#define TED_BUFFER_MAX 1024

/// Version string
#define TED_VERSION_FULL "ted v. " TED_VERSION

typedef struct {
	const char *string;
	i64 number;
} CommandArgument;

typedef struct {
	// did this command come from executing a macro?
	bool running_macro;
} CommandContext;

/// Thing to do when a key combo is pressed.
typedef struct {
	KeyCombo key_combo;
	Command command;
	CommandArgument argument;
} KeyAction;


/// A SettingsContext is a context where a specific set of settings are applied.
/// this corresponds to `[PATH//LANGUAGE.(section)]` in config files.
typedef struct {
	/// The settings apply to this language.
	Language language;
	/// The settings apply to all paths which start with this string, or all paths if path=NULL
	char *path;
} SettingsContext;

/// Need to use reference counting for textures because of Settings:
/// We copy parent settings to children
/// e.g.
/// ```
/// [core]
/// bg-texture = "blablabla.png"
/// [Javascript.core]
/// some random shit
/// ```
/// the main Settings' bg_texture will get copied to javascript's Settings,
/// so we need to be extra careful about when we delete textures.
typedef struct {
	u32 ref_count;
	GLuint texture;
} GlRcTexture;

/// Reference-counted shader-array-buffer combo.
typedef struct {
	u32 ref_count;
	GLuint shader;
	GLuint array;
	GLuint buffer;
} GlRcSAB;

typedef struct {
	Language language;
	char extension[16];
} LanguageExtension;

/// All of ted's settings
///
/// NOTE: to add more options to ted, add fields here,
/// and change the settings_<type> global constant near the top of config.c	
struct Settings {
	SettingsContext context;
	u32 colors[COLOR_COUNT];
	float cursor_blink_time_on, cursor_blink_time_off;
	float hover_time;
	float ctrl_scroll_adjust_text_size;
	float lsp_delay;
	u32 max_file_size;
	u32 max_file_size_view_only;
	u16 framerate_cap;
	u16 text_size_no_dpi;
	u16 text_size;
	u16 max_menu_width;
	u16 error_display_time;
	u16 lsp_port;
	bool auto_indent;
	bool auto_add_newline;
	bool syntax_highlighting;
	bool line_numbers;
	bool auto_reload;
	bool auto_reload_config;
	bool restore_session;
	bool regenerate_tags_if_not_found;
	bool indent_with_spaces;
	bool phantom_completions;
	bool trigger_characters;
	bool identifier_trigger_characters;
	bool signature_help_enabled;
	bool lsp_enabled;
	bool lsp_log;
	bool hover_enabled;
	bool highlight_enabled;
	bool highlight_auto;
	bool document_links;
	bool vsync;
	bool save_backup;
	bool crlf_windows;
	bool jump_to_build_error;
	bool force_monospace;
	bool show_diagnostics;
	KeyCombo hover_key;
	KeyCombo highlight_key;
	u8 tab_width;
	u8 cursor_width;
	u8 undo_save_time;
	u8 border_thickness;
	u8 padding;
	u8 scrolloff;
	u8 tags_max_depth;
	GlRcSAB *bg_shader;
	GlRcTexture *bg_texture;
	/// string used to start comments
	char comment_start[16];
	/// string used to end comments
	char comment_end[16];
	/// Comma-separated list of file names which identify the project root
	char root_identifiers[4096];
	/// LSP server command
	char lsp[512];
	/// LSP "configuration" JSON
	char lsp_configuration[4096];
	/// Build command. If non-empty, this overrides running `cargo build` if `Cargo.toml` exists, etc.
	char build_command[1024];
	/// Default build command for if `Cargo.toml`, `Makefile`, etc. do not exist.
	char build_default_command[1024];
	/// Comma separated list of paths to font files.
	char font[4096];
	/// Comma separated list of paths to bold font files.
	char font_bold[4096];
	LanguageExtension *language_extensions;
	/// dynamic array, sorted by KEY_COMBO(modifier, key)
	KeyAction *key_actions;
};

/// This structure is used temporarily when loading settings
/// It's needed because we want more specific contexts to be dealt with last.
typedef struct ConfigPart ConfigPart;

typedef struct EditNotifyInfo {
	EditNotify fn;
	void *context;
	EditNotifyID id;
} EditNotifyInfo;

/// max tabs per node
#define TED_MAX_TABS 100
/// max strings in all config files
#define TED_MAX_STRINGS 1000

/// "find" menu result
typedef struct FindResult FindResult;

typedef struct BuildError BuildError;

/// `LSPSymbolKind`s are translated to these. this is a much coarser categorization
typedef enum {
	SYMBOL_OTHER,
	SYMBOL_FUNCTION,
	SYMBOL_FIELD,
	SYMBOL_TYPE,
	SYMBOL_VARIABLE,
	SYMBOL_CONSTANT,
	SYMBOL_KEYWORD
} SymbolKind;

/// data needed for autocompletion
typedef struct Autocomplete Autocomplete;

/// data needed for finding usages
typedef struct Usages Usages;

/// data needed for formatting code
typedef struct Formatting Formatting;

/// max number of signatures to display at a time.
#define SIGNATURE_HELP_MAX 5

/// "signature help" (LSP) is thing that shows the current parameter, etc.
typedef struct SignatureHelp SignatureHelp;


/// "document link" information (LSP)
typedef struct DocumentLinks DocumentLinks;

/// information for symbol rename (LSP)
typedef struct RenameSymbol RenameSymbol;

/// "hover" information from LSP server
typedef struct Hover Hover;

/// symbol information for the definitions menu
typedef struct {
	char *name;
	char *detail;
	u32 color;
	/// is this from a LSP server (as opposed to ctags)?
	bool from_lsp;
	/// only set if `from_lsp = true`
	LSPDocumentPosition position;
} SymbolInfo;

typedef struct Definitions Definitions;

/// "highlight" information from LSP server
typedef struct Highlights Highlights;

typedef struct Macro Macro;

typedef struct LoadedFont LoadedFont;

typedef struct {
	vec2 pos;
	u8 times;
} MouseClick;

typedef struct {
	vec2 pos;
} MouseRelease;

typedef TextBuffer *TextBufferPtr;
typedef Node *NodePtr;

struct Ted {
	/// all running LSP servers
	LSP *lsps[TED_LSP_MAX + 1];
	/// current time (see time_get_seconds), as of the start of this frame
	double frame_time;
	
	Macro *macros;
	Macro *recording_macro;
	bool executing_macro;
	
	SDL_Window *window;
	LoadedFont *all_fonts;
	Font *font_bold;
	Font *font;
	TextBuffer *active_buffer;
	/// buffer we are currently drag-to-selecting in, if any
	TextBuffer *drag_buffer;
	/// while a menu or something is open, there is no active buffer. when the menu is closed,
	/// the old active buffer needs to be restored. that's what this stores.
	TextBuffer *prev_active_buffer; 
	Node *active_node;
	/// dynamic array of Settings. use Settings.context to figure out which one to use.
	Settings *all_settings;
	/// settings to use when no buffer is open
	Settings *default_settings;
	float window_width, window_height;
	vec2 mouse_pos;
	u32 mouse_state;
	/// `mouse_clicks[SDL_BUTTON_RIGHT]`, for example, is all the right mouse-clicks that have happened this frame
	MouseClick *mouse_clicks[4];
	MouseRelease *mouse_releases[4];
	/// total amount scrolled this frame
	int scroll_total_x, scroll_total_y; 
	MenuInfo *all_menus;
	/// index of currently open menu, or 0 if no menu is open
	u32 menu_open_idx;
	void *menu_context;
	FileSelector *file_selector;
	Selector *command_selector;
	/// general-purpose line buffer for inputs -- used for menus
	TextBuffer *line_buffer;
	/// use for "find" term in find/find+replace
	TextBuffer *find_buffer;
	/// "replace" for find+replace
	TextBuffer *replace_buffer;
	/// buffer for build output (view only)
	TextBuffer *build_buffer;
	/// used for command selector
	TextBuffer *argument_buffer;
	/// time which the cursor error animation started (cursor turns red, e.g. when there's no autocomplete suggestion)
	double cursor_error_time;
	/// should start_cwd be searched for files? set to true if the executable isn't "installed"
	bool search_start_cwd;
	/// CWD `ted` was started in
	char start_cwd[TED_PATH_MAX];
	/// if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!!
	bool quit;
	/// is the find or find+replace menu open?
	bool find;
	/// is the find+replace menu open?
	bool replace;
	/// find options
	bool find_regex, find_case_sensitive;
	/// flags used last time search term was compiled
	u32 find_flags;
	struct pcre2_real_code_32 *find_code;
	struct pcre2_real_match_data_32 *find_match_data;
	FindResult *find_results;
	/// invalid regex?
	bool find_invalid_pattern;
	/// if non-zero, the user is trying to execute this command, but there are unsaved changes
	Command warn_unsaved;
	/// are we showing the build output?
	bool build_shown;
	/// is the build process running?
	bool building;
	Autocomplete *autocomplete;
	SignatureHelp *signature_help;
	DocumentLinks *document_links;
	Hover *hover;
	Definitions *definitions;
	Highlights *highlights;
	Usages *usages;
	RenameSymbol *rename_symbol;
	Formatting *formatting;
	
	FILE *log;
	
	/// dynamic array of build errors
	BuildError *build_errors;
	/// build error we are currently "on"
	u32 build_error;
	
	SDL_Cursor *cursor_arrow, *cursor_ibeam, *cursor_wait,
		*cursor_resize_h, *cursor_resize_v, *cursor_hand, *cursor_move;
	/// which cursor to use this frame
	/// this should be set to one of the cursor_* members above, or NULL for no cursor
	SDL_Cursor *cursor;
	
	/// node containing tab user is dragging around, NULL if user is not dragging a tab
	Node *dragging_tab_node;
	/// index in dragging_tab_node->tabs
	u16 dragging_tab_idx;
	/// where the tab is being dragged from (i.e. mouse pos at start of drag action)
	vec2 dragging_tab_origin;
	
	/// if not `NULL`, points to the node whose split the user is currently resizing.
	Node *resizing_split;
	
	/// dynamic array of history of commands run with :shell (UTF-8)
	char **shell_history;
	/// for keeping track of where we are in the shell history.
	u32 shell_history_pos;
	/// has the shell command been modified (if so, we block up/down)
	bool shell_command_modified;

	// points to a selector if any is open, otherwise NULL.
	Selector *selector_open;
	
	/// what % of the screen the build output takes up
	float build_output_height;
	bool resizing_build_output;
	
	/// last time a save command was executed. used for bg-shaders.
	double last_save_time;

	Process *build_process;
	/// When we read the stdout from the build process, the tail end of the read could be an
	/// incomplete UTF-8 code point. This is where we store that "tail end" until more
	/// data is available. (This is up to 3 bytes, null terminated)
	char build_incomplete_codepoint[4];
	/// allows execution of multiple commands -- needed for tags generation
	char **build_queue;
	/// comma-separated list of files with unsaved changes (only applicable if warn_unsaved != 0)
	char warn_unsaved_names[TED_PATH_MAX];
	/// file name user is trying to overwrite
	char warn_overwrite[TED_PATH_MAX];
	/// file name which we want to reload
	char ask_reload[TED_PATH_MAX];
	char local_data_dir[TED_PATH_MAX];
	char global_data_dir[TED_PATH_MAX];
	/// home directory
	char home[TED_PATH_MAX];
	/// current working directory
	char cwd[TED_PATH_MAX];
	/// directory where we run the build command
	char build_dir[TED_PATH_MAX];
	/// where we are reading tags from
	char tags_dir[TED_PATH_MAX];
	/// `nodes[0]` is always the "root node", if any buffers are open.
	Node **nodes;
	TextBuffer **buffers;
	/// number of config file strings
	u32 nstrings;
	/// config file strings
	char *strings[TED_MAX_STRINGS];
	char window_title[256];
	
	/// little box used to display errors and info.
	char message[512];
	/// time message box was opened
	double message_time;
	MessageType message_type;
	MessageType message_shown_type;
	char message_shown[512];
	
	
	u64 edit_notify_id;
	EditNotifyInfo *edit_notifys;
};

// === buffer.c ===
/// create a new empty buffer with no file name
TextBuffer *buffer_new(Ted *ted);
/// create a new empty line buffer
TextBuffer *line_buffer_new(Ted *ted);
/// free all resources used by the buffer and the pointer `buffer` itself
void buffer_free(TextBuffer *buffer);
/// Does this buffer have an error?
bool buffer_has_error(TextBuffer *buffer);
/// get buffer error
const char *buffer_get_error(TextBuffer *buffer);
/// clear buffer error
void buffer_clear_error(TextBuffer *buffer);
/// Get the LSPDocumentID corresponding to the file this buffer contains.
/// The return value is only useful if `buffer_lsp(buffer) != NULL`.
LSPDocumentID buffer_lsp_document_id(TextBuffer *buffer);
/// Get LSPPosition corresponding to position in buffer.
LSPPosition buffer_pos_to_lsp_position(TextBuffer *buffer, BufferPos pos);
/// Get LSPDocumentPosition corresponding to position in buffer.
LSPDocumentPosition buffer_pos_to_lsp_document_position(TextBuffer *buffer, BufferPos pos);
/// Convert LSPPosition to BufferPos.
BufferPos buffer_pos_from_lsp(TextBuffer *buffer, LSPPosition lsp_pos);
/// Get the cursor position as an LSPPosition.
LSPPosition buffer_cursor_pos_as_lsp_position(TextBuffer *buffer);
/// Get the current selection as an LSPRange.
///
/// Returns `(LSPRange){0}` if nothing is selected.
LSPRange buffer_selection_as_lsp_range(TextBuffer *buffer);
/// Apply LSP TextEdit[] from response
void buffer_apply_lsp_text_edits(TextBuffer *buffer, const LSPResponse *response, const LSPTextEdit *edits, size_t n_edits);
/// Get the cursor position as an LSPDocumentPosition.
LSPDocumentPosition buffer_cursor_pos_as_lsp_document_position(TextBuffer *buffer);
/// highlight an \ref LSPRange in this buffer.
///
/// make sure to call \ref gl_geometry_draw after this
void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range, ColorSetting color);
/// process a mouse click.
/// returns true if the event was consumed.
bool buffer_handle_click(Ted *ted, TextBuffer *buffer, vec2 click, u8 times);
/// next frame, scroll so that the cursor is in the center of the buffer's rectangle.
///
/// currently needed to avoid bad positioning when a buffer is created
/// and buffer_center_cursor is called immediately after
void buffer_center_cursor_next_frame(TextBuffer *buffer);
/// perform a series of checks to make sure the buffer doesn't have any invalid values
void buffer_check_valid(TextBuffer *buffer);
void buffer_publish_diagnostics(TextBuffer *buffer, const LSPRequest *request, LSPDiagnostic *diagnostics);

// === build.c ===
void build_frame(Ted *ted, float x1, float y1, float x2, float y2);

// === colors.c ====
void color_init(void);
/// which color setting should be used for the given symbol kind.
/// this is the color used in the autocomplete selector, for example.
ColorSetting color_for_symbol_kind(SymbolKind kind);

// === command.c ===
void command_init(void);
void command_execute_ex(Ted *ted, Command c, const CommandArgument *argument, const CommandContext *context);

// === config.c ===
/// first, we read all config files, then we parse them.
/// this is because we want less specific settings (e.g. settings applied
/// to all languages instead of one particular language) to be applied first,
/// then more specific settings are based off of those.
///
/// EXAMPLE:
/// ```
///   ---config file 1---
///     [Javascript.core]
///     syntax-highlighting = off
///     (inherits tab-width = 4)
///     [CSS.core]
///     tab-width = 2 (overrides tab-width = 4)
///   ---config file 2---
///     [core]
///     tab-width = 4
/// ```
void config_read(Ted *ted, ConfigPart **pparts, const char *filename);
void config_parse(Ted *ted, ConfigPart **pparts);
void config_free(Ted *ted);
/// how well does this settings context fit the given path and language?
/// the context with the highest score will be chosen.
long context_score(const char *path, Language lang, const SettingsContext *context);

// === find.c ===
void find_init(Ted *ted);
/// height of the find/find+replace menu in pixels
float find_menu_height(Ted *ted);
void find_menu_frame(Ted *ted, Rect menu_bounds);

// === gl.c ===
/// set by main()
extern float gl_window_width, gl_window_height;
/// set by main()
extern int gl_version_major, gl_version_minor;
/// macro trickery to avoid having to write every GL function multiple times
#define gl_for_each_proc(do)\
	do(DRAWARRAYS, DrawArrays)\
	do(GENTEXTURES, GenTextures)\
	do(DELETETEXTURES, DeleteTextures)\
	do(GENERATEMIPMAP, GenerateMipmap)\
	do(TEXIMAGE2D, TexImage2D)\
	do(BINDTEXTURE, BindTexture)\
	do(TEXPARAMETERI, TexParameteri)\
	do(GETERROR, GetError)\
	do(GETINTEGERV, GetIntegerv)\
	do(ENABLE, Enable)\
	do(DISABLE, Disable)\
	do(BLENDFUNC, BlendFunc)\
	do(VIEWPORT, Viewport)\
	do(CLEARCOLOR, ClearColor)\
	do(CLEAR, Clear)\
	do(FINISH, Finish)\
	do(CREATESHADER, CreateShader)\
	do(DELETESHADER, DeleteShader)\
	do(CREATEPROGRAM, CreateProgram)\
	do(SHADERSOURCE, ShaderSource)\
	do(GETSHADERIV, GetShaderiv)\
	do(GETSHADERINFOLOG, GetShaderInfoLog)\
	do(COMPILESHADER, CompileShader)\
	do(CREATEPROGRAM, CreateProgram)\
	do(DELETEPROGRAM, DeleteProgram)\
	do(ATTACHSHADER, AttachShader)\
	do(LINKPROGRAM, LinkProgram)\
	do(GETPROGRAMIV, GetProgramiv)\
	do(GETPROGRAMINFOLOG, GetProgramInfoLog)\
	do(USEPROGRAM, UseProgram)\
	do(GETATTRIBLOCATION, GetAttribLocation)\
	do(GETUNIFORMLOCATION, GetUniformLocation)\
	do(GENBUFFERS, GenBuffers)\
	do(DELETEBUFFERS, DeleteBuffers)\
	do(BINDBUFFER, BindBuffer)\
	do(BUFFERDATA, BufferData)\
	do(VERTEXATTRIBPOINTER, VertexAttribPointer)\
	do(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray)\
	do(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray)\
	do(GENVERTEXARRAYS, GenVertexArrays)\
	do(DELETEVERTEXARRAYS, DeleteVertexArrays)\
	do(BINDVERTEXARRAY, BindVertexArray)\
	do(ACTIVETEXTURE, ActiveTexture)\
	do(UNIFORM1F, Uniform1f)\
	do(UNIFORM2F, Uniform2f)\
	do(UNIFORM3F, Uniform3f)\
	do(UNIFORM4F, Uniform4f)\
	do(UNIFORM1I, Uniform1i)\
	do(UNIFORM2I, Uniform2i)\
	do(UNIFORM3I, Uniform3i)\
	do(UNIFORM4I, Uniform4i)\
	do(UNIFORMMATRIX4FV, UniformMatrix4fv)\
	do(DEBUGMESSAGECALLBACK, DebugMessageCallback)\
	do(DEBUGMESSAGECONTROL, DebugMessageControl)\

#define gl_declare_proc(upper, lower) extern PFNGL##upper##PROC gl##lower;
gl_for_each_proc(gl_declare_proc)
#undef gl_declare_proc
/// get addresses of GL functions
void gl_get_procs(void);
/// create a new reference-counted shader-array-buffer object.
GlRcSAB *gl_rc_sab_new(GLuint shader, GLuint array, GLuint buffer);
/// increase reference count on `s`.
void gl_rc_sab_incref(GlRcSAB *s);
/// decrease reference count on `*ps`, and set `*ps` to NULL if the reference count is 0.
void gl_rc_sab_decref(GlRcSAB **ps);
/// create a new reference-counted texture.
GlRcTexture *gl_rc_texture_new(GLuint texture);
/// increase reference count on `t`.
void gl_rc_texture_incref(GlRcTexture *t);
/// decrease reference count on `*t`, and set `*t` to NULL if the reference count is 0.
void gl_rc_texture_decref(GlRcTexture **pt);
/// initialize geometry stuff
void gl_geometry_init(void);

// === ide-autocomplete.c ===
void autocomplete_init(Ted *ted);
void autocomplete_quit(Ted *ted);
void autocomplete_frame(Ted *ted);
void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response);

// === ide-definitions.c ===
void definitions_init(Ted *ted);
/// go to the definition of `name`.
/// if `lsp` is NULL, tags will be used.
/// Note: the document position is required for LSP requests because of overloading (where the name
/// alone isn't sufficient)
void definition_goto(Ted *ted, LSP *lsp, const char *name, LSPDocumentPosition pos, GotoType type);
void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *response);
void definitions_frame(Ted *ted);
void definitions_quit(Ted *ted);

// === ide-document-link.c ===
void document_link_init(Ted *ted);
void document_link_quit(Ted *ted);
void document_link_frame(Ted *ted);
void document_link_process_lsp_response(Ted *ted, const LSPResponse *response);


// === ide-format.c ===
/// initialize formatting stuff
void format_init(Ted *ted);
void format_process_lsp_response(Ted *ted, const LSPResponse *response);
/// cancel last formatting request
void format_cancel_request(Ted *ted);
/// free formatting stuff
void format_quit(Ted *ted);

// === ide-highlights.c ===
void highlights_init(Ted *ted);
void highlights_quit(Ted *ted);
void highlights_frame(Ted *ted);
void highlights_process_lsp_response(Ted *ted, const LSPResponse *response);

// === ide-hover.c ===
void hover_init(Ted *ted);
void hover_frame(Ted *ted, double dt);
void hover_process_lsp_response(Ted *ted, const LSPResponse *response);
void hover_quit(Ted *ted);

// === ide-rename-symbol.c ===
void rename_symbol_init(Ted *ted);
void rename_symbol_quit(Ted *ted);
void rename_symbol_frame(Ted *ted);
void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response);

// === ide-signature-help.c ===
void signature_help_init(Ted *ted);
void signature_help_quit(Ted *ted);
void signature_help_frame(Ted *ted);
void signature_help_process_lsp_response(Ted *ted, const LSPResponse *response);

// === ide-usages.c ===
void usages_init(Ted *ted);
void usages_process_lsp_response(Ted *ted, const LSPResponse *response);
void usages_frame(Ted *ted);
void usages_quit(Ted *ted);

// === macro.c ===
void macro_add(Ted *ted, Command command, const CommandArgument *argument);
void macros_init(Ted *ted);
void macros_free(Ted *ted);

// === menu.c ===
void menu_init(Ted *ted);
void menu_quit(Ted *ted);
void menu_update(Ted *ted);
void menu_render(Ted *ted);
/// move to next/previous command
void menu_shell_move(Ted *ted, int direction);
/// move to previous command
void menu_shell_up(Ted *ted);
/// move to next command
void menu_shell_down(Ted *ted);
Rect selection_menu_render_bg(Ted *ted);

// === node.c ===
Node *node_new(Ted *ted);
void node_free(Node *node);
/// don't call this if `buffer` is in any other nodes!
///
/// \returns false if there are too many tabs
Status node_add_tab(Ted *ted, Node *node, TextBuffer *buffer);
/// cannot be called if `node` has already been initialized or contains tabs.
void node_init_split(Node *node, Node *child1, Node *child2, float split_pos, bool is_vertical);
void node_frame(Ted *ted, Node *node, Rect r);

// === syntax.c ===
/// register built-in languages, etc.
void syntax_init(void);
/// free up resources used by `syntax.c`
void syntax_quit(void);

// === tags.c ===
/// get all tags in the tags file as SymbolInfos.
SymbolInfo *tags_get_symbols(Ted *ted);

// === ted.c ===
/// set ted's active buffer to something nice
void ted_reset_active_buffer(Ted *ted);
/// set ted's error message to the buffer's error.
void ted_error_from_buffer(Ted *ted, TextBuffer *buffer);
/// Get LSP by ID. Returns NULL if there is no LSP with that ID.
LSP *ted_get_lsp_by_id(Ted *ted, LSPID id);
/// go to this LSP document position, opening a new buffer containing the file if necessary.
void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition position);
/// cancel this LSP request. also zeroes *request
/// if *request is zeroed, this does nothing.
void ted_cancel_lsp_request(Ted *ted, LSPServerRequestID *request);
/// convert LSPWindowMessageType to MessageType
MessageType ted_message_type_from_lsp(LSPWindowMessageType type);
/// delete buffer - does NOT remove it from the node tree
void ted_delete_buffer(Ted *ted, TextBuffer *buffer);
/// Returns a new buffer, or NULL on out of memory
TextBuffer *ted_new_buffer(Ted *ted);
/// check for orphaned nodes and node cycles
void ted_check_for_node_problems(Ted *ted);
/// load ted configuration
void ted_load_configs(Ted *ted);
/// get colors to use for message box
void ted_color_settings_for_message_type(MessageType type, ColorSetting *bg_color, ColorSetting *border_color);
/// Load all the fonts ted will use, freeing any previous ones.
void ted_load_fonts(Ted *ted);
/// Free all of ted's fonts.
void ted_free_fonts(Ted *ted);
/// process textDocument/publishDiagnostics request
void ted_process_publish_diagnostics(Ted *ted, LSP *lsp, LSPRequest *request);

#endif // TED_INTERNAL_H_