/*
 * Copyright (C) 2012-2024 Robin Haberkorn
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include 
#include 
#include "sciteco.h"
#include "string-utils.h"
#include "interface.h"
#include "list.h"
#include "error.h"
guint teco_error_return_args = 0;
/*
 * FIXME: Does this have to be stored in teco_machine_main_t?
 * Probably becomes clear once we implement error handling by macros.
 */
guint teco_error_pos = 0, teco_error_line = 0, teco_error_column = 0;
typedef enum {
	TECO_FRAME_QREG,
	TECO_FRAME_FILE,
	TECO_FRAME_EDHOOK,
	TECO_FRAME_TOPLEVEL
} teco_frame_type_t;
typedef struct teco_frame_t {
	teco_stailq_entry_t entry;
	teco_frame_type_t type;
	guint pos, line, column;
	/*
	 * NOTE: This is currently sufficient to describe all
	 * frame types. Otherwise, add an union.
	 */
	gchar name[];
} teco_frame_t;
/**
 * List of teco_frame_t describing the stack frames.
 *
 * Stack frames are collected deliberately unformatted
 * since there are future applications where displaying
 * a stack frame will not be necessary (e.g. error handled
 * by SciTECO macro).
 * Preformatting all stack frames would be very costly.
 */
static teco_stailq_head_t teco_frames = TECO_STAILQ_HEAD_INITIALIZER(&teco_frames);
void
teco_error_display_short(const GError *error)
{
	teco_interface_msg(TECO_MSG_ERROR, "%s (at %d)",
	                   error->message, teco_error_pos);
}
void
teco_error_display_full(const GError *error)
{
	teco_interface_msg(TECO_MSG_ERROR, "%s", error->message);
	guint nr = 0;
	for (teco_stailq_entry_t *cur = teco_frames.first; cur != NULL; cur = cur->next) {
		teco_frame_t *frame = (teco_frame_t *)cur;
		switch (frame->type) {
		case TECO_FRAME_QREG:
			teco_interface_msg(TECO_MSG_INFO,
			                   "#%d in Q-Register \"%s\" at %d (%d:%d)",
			                   nr, frame->name, frame->pos, frame->line, frame->column);
			break;
		case TECO_FRAME_FILE:
			teco_interface_msg(TECO_MSG_INFO,
			                   "#%d in file \"%s\" at %d (%d:%d)",
			                   nr, frame->name, frame->pos, frame->line, frame->column);
			break;
		case TECO_FRAME_EDHOOK:
			teco_interface_msg(TECO_MSG_INFO,
			                   "#%d in \"%s\" hook execution",
			                   nr, frame->name);
			break;
		case TECO_FRAME_TOPLEVEL:
			teco_interface_msg(TECO_MSG_INFO,
			                   "#%d in toplevel macro at %d (%d:%d)",
			                   nr, frame->pos, frame->line, frame->column);
			break;
		}
		nr++;
	}
}
static teco_frame_t *
teco_error_add_frame(teco_frame_type_t type, gsize size)
{
	teco_frame_t *frame = g_malloc(sizeof(teco_frame_t) + size);
	frame->type	= type;
	frame->pos	= teco_error_pos;
	frame->line	= teco_error_line;
	frame->column	= teco_error_column;
	teco_stailq_insert_tail(&teco_frames, &frame->entry);
	return frame;
}
void
teco_error_add_frame_qreg(const gchar *name, gsize len)
{
	g_autofree gchar *name_printable = teco_string_echo(name, len);
	teco_frame_t *frame = teco_error_add_frame(TECO_FRAME_QREG, strlen(name_printable) + 1);
	strcpy(frame->name, name_printable);
}
void
teco_error_add_frame_file(const gchar *name)
{
	teco_frame_t *frame = teco_error_add_frame(TECO_FRAME_FILE, strlen(name) + 1);
	strcpy(frame->name, name);
}
void
teco_error_add_frame_edhook(const gchar *type)
{
	teco_frame_t *frame = teco_error_add_frame(TECO_FRAME_EDHOOK, strlen(type) + 1);
	strcpy(frame->name, type);
}
void
teco_error_add_frame_toplevel(void)
{
	teco_error_add_frame(TECO_FRAME_TOPLEVEL, 0);
}
void TECO_DEBUG_CLEANUP
teco_error_clear_frames(void)
{
	teco_stailq_entry_t *entry;
	while ((entry = teco_stailq_remove_head(&teco_frames)))
		g_free(entry);
}