static const char *refdes(const csch_cgrp_t *g)
{
	return csch_attrib_get_str(&g->attr, "name");
}

static void renumber_list_objs_sheet(renumber_t *rctx, csch_sheet_t *sheet)
{
	htip_entry_t *e;

	for(e = htip_first(&sheet->direct.id2obj); e != NULL; e = htip_next(&sheet->direct.id2obj, e)) {
		csch_cgrp_t *g = e->value;
		const char *r;

		if (!csch_obj_is_grp(&g->hdr) || (g->role != CSCH_ROLE_SYMBOL))
			continue;

		r = refdes(g);

	 /* unnamed symbols shouldn't be named by renumber - the user did not
	    want these symbols to be on the output */
		if (r == NULL)
			continue;

		htsp_insert(&rctx->name2sym, (char *)r, g);
		if (!rctx->selected || g->hdr.selected) {
			vtp0_append(&rctx->objs, g);
			g->hdr.mark = 1; /* objects we are going to change are marked */
		}
	}
}

static void renumber_list_objs(renumber_t *rctx)
{
	if (rctx->inprj) {
		rnd_project_t *prj = rctx->sheet->hidlib.project;
		long n;
		for(n = 0; n < prj->designs.used; n++)
			renumber_list_objs_sheet(rctx, prj->designs.array[n]);
	}
	else
		renumber_list_objs_sheet(rctx, rctx->sheet);
}


static int cmp_tb(const void *v1, const void *v2);

static int cmp_lr(const void *v1, const void *v2)
{
	const csch_cgrp_t **s1 = (const csch_cgrp_t **)v1, **s2 = (const csch_cgrp_t **)v2;
	csch_coord_t x1 = (*s1)->hdr.bbox.x1 + (*s1)->hdr.bbox.x2, x2 = (*s2)->hdr.bbox.x1 + (*s2)->hdr.bbox.x2;
	if ((*s1)->hdr.sheet->uid < (*s2)->hdr.sheet->uid) return -1;
	if ((*s1)->hdr.sheet->uid > (*s2)->hdr.sheet->uid) return +1;
	if (x1 == x2) return cmp_tb(v1, v2);
	return (x1 > x2);
}


static int cmp_rl(const void *v1, const void *v2)
{
	const csch_cgrp_t **s1 = (const csch_cgrp_t **)v1, **s2 = (const csch_cgrp_t **)v2;
	csch_coord_t x1 = (*s1)->hdr.bbox.x1 + (*s1)->hdr.bbox.x2, x2 = (*s2)->hdr.bbox.x1 + (*s2)->hdr.bbox.x2;
	if ((*s1)->hdr.sheet->uid < (*s2)->hdr.sheet->uid) return -1;
	if ((*s1)->hdr.sheet->uid > (*s2)->hdr.sheet->uid) return +1;
	if (x1 == x2) return cmp_tb(v1, v2);
	return (x1 < x2);
}

static int cmp_tb(const void *v1, const void *v2)
{
	const csch_cgrp_t **s1 = (const csch_cgrp_t **)v1, **s2 = (const csch_cgrp_t **)v2;
	csch_coord_t y1 = (*s1)->hdr.bbox.y1 + (*s1)->hdr.bbox.y2, y2 = (*s2)->hdr.bbox.y1 + (*s2)->hdr.bbox.y2;
	if ((*s1)->hdr.sheet->uid < (*s2)->hdr.sheet->uid) return -1;
	if ((*s1)->hdr.sheet->uid > (*s2)->hdr.sheet->uid) return +1;
	if (y1 == y2) return cmp_lr(v1, v2);
	return (y1 < y2);
}

static int cmp_bt(const void *v1, const void *v2)
{
	const csch_cgrp_t **s1 = (const csch_cgrp_t **)v1, **s2 = (const csch_cgrp_t **)v2;
	csch_coord_t y1 = (*s1)->hdr.bbox.y1 + (*s1)->hdr.bbox.y2, y2 = (*s2)->hdr.bbox.y1 + (*s2)->hdr.bbox.y2;
	if ((*s1)->hdr.sheet->uid < (*s2)->hdr.sheet->uid) return -1;
	if ((*s1)->hdr.sheet->uid > (*s2)->hdr.sheet->uid) return +1;
	if (y1 == y2) return cmp_lr(v1, v2);
	return (y1 > y2);
}


static int renumber_sort(renumber_t *rctx)
{
	int (*cmp)(const void *sym1, const void *sym2);

	if (strcmp(rctx->sorta, "lr") == 0) cmp = cmp_lr;
	else if (strcmp(rctx->sorta, "rl") == 0) cmp = cmp_rl;
	else if (strcmp(rctx->sorta, "tb") == 0) cmp = cmp_tb;
	else if (strcmp(rctx->sorta, "bt") == 0) cmp = cmp_bt;
	else
		return -1;

	qsort(rctx->objs.array, rctx->objs.used, sizeof(csch_cgrp_t *), cmp);
	return 0;
}

/* renders next available refdes for prefix in prefix */
static void set_next_num(renumber_t *rctx, char *prefix, char *flex)
{
	htsi_entry_t *e;
	int n;


	e = htsi_getentry(&rctx->next, prefix);
	if (e == NULL) {
		htsi_set(&rctx->next, rnd_strdup(prefix), rctx->base);
		e = htsi_getentry(&rctx->next, prefix);
	}

	for(n = 0; n < 32767; n++) {
		csch_cgrp_t *user;

		sprintf(flex, "%d", e->value);
		e->value++;

		user = htsp_get(&rctx->name2sym, prefix);
		if ((user == NULL) || user->hdr.mark) /* unused or used by something we are going to rename anyway */
			return; /* accept this name */
	}
	strcpy(flex, "??"); /* ran out of numbers... shouldn't happen */
}

static int renumber_sym(renumber_t *rctx, csch_cgrp_t *sym)
{
	csch_source_arg_t *src;
	char *end, *r;
	const char *orig = refdes(sym);
	int len = strlen(orig);

	r = malloc(len + 16); /* enough room for appending %ld */
	memcpy(r, orig, len+1);

	end = r + len - 1;
	while((end >= r) && ((*end == '?') || isdigit(*end))) end--;

	if (end < r) {
		r[0] = 'U';
		r[1] = '\0';
		end = r+1;
	}
	else {
		end++;
		*end = '\0';
	}

	set_next_num(rctx, r, end);
	src = csch_attrib_src_p("renumber", "Renumber()");

	csch_attr_modify_str(sym->hdr.sheet, sym, CSCH_ATP_USER_DEFAULT, "name", r, src, 1);
	free(r);
	return 0;
}

static int renumber_objs(renumber_t *rctx)
{
	long n;
	rnd_project_t *prj = rctx->sheet->hidlib.project;

	if (rctx->inprj) {
		for(n = 0; n < prj->designs.used; n++) {
			csch_sheet_t *sheet = prj->designs.array[n];
			uundo_freeze_serial(&sheet->undo);
		}
	}
	else
		uundo_freeze_serial(&rctx->sheet->undo);

	for(n = 0; n < rctx->objs.used; n++)
		renumber_sym(rctx, rctx->objs.array[n]);

	if (rctx->inprj) {
		for(n = 0; n < prj->designs.used; n++) {
			csch_sheet_t *sheet = prj->designs.array[n];
			uundo_unfreeze_serial(&sheet->undo);
			uundo_inc_serial(&sheet->undo);
		}
	}
	else {
		uundo_unfreeze_serial(&rctx->sheet->undo);
		uundo_inc_serial(&rctx->sheet->undo);
	}

	for(n = 0; n < rctx->objs.used; n++) {
		csch_cgrp_t *g = rctx->objs.array[n];
		g->hdr.mark = 0;
	}
	return 0;
}
