/*
 * Copyright (C) Birb Developers
 *
 * Birb is the legal property of its developers, whose names are too numerous
 * to list here. Please refer to the AUTHORS file distributed with this source
 * distribution
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This library 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "birbpangoformatter.h"

#include "birbstr.h"

/*****************************************************************************
 * Helpers
 ****************************************************************************/
static void
birb_pango_formatter_attribute_start(BirbPangoFormatter *formatter,
                                     PangoAttribute *attribute)
{
	BirbPangoFormatterClass *klass = NULL;

	g_return_if_fail(BIRB_IS_PANGO_FORMATTER(formatter));

	klass = BIRB_PANGO_FORMATTER_GET_CLASS(formatter);
	if(klass != NULL && klass->attribute_start != NULL) {
		klass->attribute_start(formatter, attribute);
	}
}

static void
birb_pango_formatter_attribute_end(BirbPangoFormatter *formatter,
                                   PangoAttribute *attribute)
{
	BirbPangoFormatterClass *klass = NULL;

	g_return_if_fail(BIRB_IS_PANGO_FORMATTER(formatter));

	klass = BIRB_PANGO_FORMATTER_GET_CLASS(formatter);
	if(klass != NULL && klass->attribute_end != NULL) {
		klass->attribute_end(formatter, attribute);
	}
}

static void
birb_pango_formatter_text(BirbPangoFormatter *formatter, const char *text) {
	BirbPangoFormatterClass *klass = NULL;

	g_return_if_fail(BIRB_IS_PANGO_FORMATTER(formatter));
	g_return_if_fail(text != NULL);

	klass = BIRB_PANGO_FORMATTER_GET_CLASS(formatter);
	if(klass != NULL && klass->text != NULL) {
		klass->text(formatter, text);
	}
}

static void
birb_pango_formatter_format_internal(BirbPangoFormatter *formatter,
                                     const char *plain_text,
                                     PangoAttrList *attrs)
{
	PangoAttrIterator *attr_iter = NULL;

	/* If we don't have any attributes, just call text with the plain text. */
	if(attrs == NULL) {
		birb_pango_formatter_text(formatter, plain_text);

		return;
	}

	/* We have attributes, so we need to iterate them. */
	attr_iter = pango_attr_list_get_iterator(attrs);
	do {
		GSList *attributes_opening = NULL;
		GSList *attributes_closing = NULL;
		char *text = NULL;
		int start_index = 0;
		int end_index = 0;

		pango_attr_iterator_range(attr_iter, &start_index, &end_index);

		if(start_index == G_MAXINT) {
			break;
		}

		/* Call the start method for all of the attributes. */
		attributes_opening = pango_attr_iterator_get_attrs(attr_iter);
		while(attributes_opening != NULL) {
			PangoAttribute *attribute = attributes_opening->data;

			birb_pango_formatter_attribute_start(formatter, attribute);

			attributes_closing = g_slist_prepend(attributes_closing,
			                                     attribute);

			attributes_opening = g_slist_delete_link(attributes_opening,
			                                         attributes_opening);
		}

		/* Call the text method for the text. */
		if(end_index == G_MAXINT) {
			text = g_strdup(plain_text + start_index);
		} else {
			text = g_strndup(plain_text + start_index,
			                 end_index - start_index);
		}
		birb_pango_formatter_text(formatter, text);
		g_free(text);

		/* Walk through the attributes in reverse order to end them. */
		while(attributes_closing != NULL) {
			PangoAttribute *attribute = attributes_closing->data;

			birb_pango_formatter_attribute_end(formatter, attribute);

			pango_attribute_destroy(attribute);

			attributes_closing = g_slist_delete_link(attributes_closing,
			                                         attributes_closing);
		}
	} while(pango_attr_iterator_next(attr_iter));

	g_clear_pointer(&attr_iter, pango_attr_iterator_destroy);
}

/*****************************************************************************
 * GObject Implementation
 ****************************************************************************/
G_DEFINE_ABSTRACT_TYPE(BirbPangoFormatter, birb_pango_formatter,
                       G_TYPE_OBJECT)

static void
birb_pango_formatter_init(G_GNUC_UNUSED BirbPangoFormatter *formatter) {
}

static void
birb_pango_formatter_class_init(G_GNUC_UNUSED BirbPangoFormatterClass *klass)
{
}

/*****************************************************************************
 * Public API
 ****************************************************************************/
char *
birb_pango_formatter_format(BirbPangoFormatter *formatter,
                            const char *plain_text,
                            PangoAttrList *attrs)
{
	BirbPangoFormatterClass *klass = NULL;

	g_return_val_if_fail(BIRB_IS_PANGO_FORMATTER(formatter), NULL);
	g_return_val_if_fail(!birb_str_is_empty(plain_text), NULL);

	klass = BIRB_PANGO_FORMATTER_GET_CLASS(formatter);
	if(klass != NULL && klass->start != NULL) {
		klass->start(formatter);
	}

	birb_pango_formatter_format_internal(formatter, plain_text, attrs);

	if(klass != NULL && klass->finish != NULL) {
		return klass->finish(formatter);
	}

	return NULL;
}

