/*
	passage.c

	Text passage formatting.

	Copyright (C) 2022  Bill Currie <bill@taniwha.org>

	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 2
	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, write to:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#include "QF/alloc.h"
#include "QF/qtypes.h"
#include "QF/sys.h"

#include "QF/ecs.h"

#include "QF/ui/passage.h"
#include "QF/ui/view.h"

const component_t passage_components[passage_comp_count] = {
	[passage_href] = {
		.size = sizeof (hierref_t),
		.name = "passage href",
		.destroy = Hierref_DestroyComponent,
	},
};

static const component_t passage_type_components[passage_type_count] = {
	[passage_type_text_obj] = {
		.size = sizeof (psg_text_t),
		.name = "Text",
	},
};

static const hierarchy_type_t passage_type = {
	.num_components = passage_type_count,
	.components = passage_type_components,
};

VISIBLE int
Passage_IsSpace (const char *text)
{
	if (text[0] == ' ') {
		return 1;
	}
	// 2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	// 2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	// 2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	// 2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	// 2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	// 2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	// 2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	// 200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	if ((byte)text[0] == 0xe2 && (byte)text[1] == 0x80
		&& ((byte)text[2] >= 0x80 && (byte)text[2] < 0x90
			&& ((1 << (text[2] & 0xf)) & 0x077c))) {
		return 3;
	}
	// 205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
	if ((byte)text[0] == 0xe2 && (byte)text[1] == 0x81
		&& (byte)text[2] == 0x9f) {
		return 3;
	}
	return 0;
}

static void
add_entity (hierarchy_t *h, uint32_t parent)
{
	uint32_t    i = Hierarchy_InsertHierarchy (h, 0, parent, 0);
	h->ent[i] = ECS_NewEntity (h->reg);
	hierref_t  *ref = Ent_AddComponent (h->ent[i], h->href_comp, h->reg);
	ref->hierarchy = h;
	ref->index = i;
}

VISIBLE void
Passage_ParseText (passage_t *passage, const char *text)
{
	if (passage->hierarchy) {//FIXME just empty hierarchy
		Hierarchy_Delete (passage->hierarchy);
	}
	if (!*text) {
		return;
	}
	passage->text = text;

	unsigned    num_paragraphs = 1;
	unsigned    num_text_objects = 1;
	psg_text_t  root_text = {};
	int         parsing_space = Passage_IsSpace (text);
	for (const char *c = text; *c; c++) {
		int         size;
		if ((size = Passage_IsSpace (c))) {
			if (!parsing_space) {
				num_text_objects++;
			}
			parsing_space = 1;
			c += size - 1;
		} else if (*c == '\n') {
			if (c[1]) {
				num_paragraphs++;
				num_text_objects += !Passage_IsSpace (c + 1);
			}
		} else {
			if (parsing_space) {
				num_text_objects++;
			}
			parsing_space = 0;
		}
		root_text.size = c - text;
	}
	passage->hierarchy = Hierarchy_New (passage->reg,
										passage->comp_base + passage_href,
										&passage_type, 0);
	Hierarchy_Reserve (passage->hierarchy,
					   1 + num_paragraphs + num_text_objects);
#if 0
	printf ("num_paragraphs %d, num_text_objects %d\n", num_paragraphs,
			passage->num_text_objects);
#endif
	add_entity (passage->hierarchy, nullent);
	for (unsigned i = 0; i < num_paragraphs; i++) {
		add_entity (passage->hierarchy, 0);
	}

	num_paragraphs = 0;
	hierarchy_t *h = passage->hierarchy;
	psg_text_t *passage_obj = h->components[passage_type_text_obj];
	psg_text_t *par_obj = &passage_obj[h->childIndex[0]];
	psg_text_t *text_obj = &passage_obj[h->childIndex[1]];
	*par_obj = *text_obj = (psg_text_t) { };

	*passage_obj = root_text;
	add_entity (passage->hierarchy, par_obj - passage_obj);

	parsing_space = Passage_IsSpace (text);
	for (const char *c = text; *c; c++) {
		int         size;
		if ((size = Passage_IsSpace (c))) {
			if (!parsing_space) {
				add_entity (passage->hierarchy, par_obj - passage_obj);
				text_obj->size = c - text - text_obj->text;
				(++text_obj)->text = c - text;
			}
			parsing_space = 1;
			c += size - 1;
		} else if (*c == '\n') {
			text_obj->size = c - text - text_obj->text;
			par_obj->size = c - text - par_obj->text;
			if (c[1]) {
				(++par_obj)->text = c + 1 - text;
				add_entity (passage->hierarchy, par_obj - passage_obj);
				(++text_obj)->text = c + 1 - text;
				parsing_space = Passage_IsSpace (c + 1);
			}
		} else {
			if (parsing_space) {
				add_entity (passage->hierarchy, par_obj - passage_obj);
				text_obj->size = c - text - text_obj->text;
				(++text_obj)->text = c - text;
			}
			parsing_space = 0;
			if (!c[1]) {
				text_obj->size = c + 1 - text - text_obj->text;
				par_obj->size = c + 1 - text - par_obj->text;
				passage_obj->size = c + 1 - text - passage_obj->text;
			}
		}
	}
#if 0
	for (uint32_t i = 0; i < h->num_objects; i++) {
		psg_text_t *to = &passage_obj[i];
		uint32_t    ent = h->ent[i];
		hierref_t  *ref = Ent_GetComponent (ent, h->href_comp, reg);
		printf ("%3d %8x %3d %4d %4d '%.*s'\n", i, ent, ref->index,
				to->text, to->size, to->size, text + to->text);
	}
#endif
}

VISIBLE passage_t *
Passage_New (ecs_system_t passage_sys)
{
	passage_t  *passage = malloc (sizeof (passage_t));
	passage->text = 0;
	passage->reg = passage_sys.reg;
	passage->comp_base = passage_sys.base;
	passage->hierarchy = 0;
	return passage;
}

VISIBLE void
Passage_Delete (passage_t *passage)
{
	if (passage->hierarchy) {
		Hierarchy_Delete (passage->hierarchy);
	}
	free (passage);
}