text: // SPDX-FileCopyrightText: 2019 pancake <pancake@nopcode.org>
  // SPDX-FileCopyrightText: 2019 ret2libc <sirmy15@gmail.com>
  // SPDX-License-Identifier: LGPL-3.0-only
  
  #include "rz_util/rz_spaces.h"
  
  RZ_API RzSpaces *rz_spaces_new(const char *name) {
  	RzSpaces *sp = RZ_NEW0(RzSpaces);
  	if (!sp || !rz_spaces_init(sp, name)) {
  		free(sp);
  		return NULL;
  	}
  	return sp;
  }
  
  RZ_API bool rz_spaces_init(RzSpaces *sp, const char *name) {
  	rz_return_val_if_fail(sp && name, false);
  	sp->name = strdup(name);
  	if (!sp->name) {
  		goto fail;
  	}
  
  	sp->spaces = NULL;
  	sp->current = NULL;
  	sp->spacestack = rz_list_new();
  	if (!sp->spacestack) {
  		goto fail;
  	}
  
  	sp->event = rz_event_new(sp);
  	if (!sp->event) {
  		goto fail;
  	}
  
  	return true;
  
  fail:
  	rz_spaces_fini(sp);
  	return false;
  }
  
  RZ_API void rz_spaces_free(RzSpaces *sp) {
  	rz_spaces_fini(sp);
  	free(sp);
  }
  
  static inline void space_free(RzSpace *s) {
  	if (s) {
  		free(s->name);
  		free(s);
  	}
  }
  
  static void space_node_free(RBNode *n, void *user) {
  	RzSpace *s = container_of(n, RzSpace, rb);
  	space_free(s);
  }
  
  RZ_API void rz_spaces_fini(RzSpaces *sp) {
  	rz_list_free(sp->spacestack);
  	sp->spacestack = NULL;
  	rz_rbtree_free(sp->spaces, space_node_free, NULL);
  	sp->spaces = NULL;
  	rz_event_free(sp->event);
  	sp->event = NULL;
  	sp->current = NULL;
  	RZ_FREE(sp->name);
  }
  
  RZ_API void rz_spaces_purge(RzSpaces *sp) {
  	sp->current = NULL;
  	rz_list_purge(sp->spacestack);
  	rz_rbtree_free(sp->spaces, space_node_free, NULL);
  	sp->spaces = NULL;
  }
  
  static int name_space_cmp(const void *incoming, const RBNode *rb, void *user) {
  	const RzSpace *s = container_of(rb, const RzSpace, rb);
  	return strcmp(incoming, s->name);
  }
  
  RZ_API RzSpace *rz_spaces_get(RzSpaces *sp, const char *name) {
  	if (!name) {
  		return NULL;
  	}
  	RBNode *n = rz_rbtree_find(sp->spaces, (void *)name, name_space_cmp, NULL);
  	return n ? container_of(n, RzSpace, rb) : NULL;
  }
  
  static int space_cmp(const void *incoming, const RBNode *rb, void *user) {
  	const RzSpace *a = (const RzSpace *)incoming;
  	const RzSpace *b = container_of(rb, const RzSpace, rb);
  	return strcmp(a->name, b->name);
  }
  
  RZ_API RzSpace *rz_spaces_add(RzSpaces *sp, const char *name) {
  	rz_return_val_if_fail(sp, NULL);
  	if (!name || !*name || *name == '*') {
  		return NULL;
  	}
  
  	RzSpace *s = rz_spaces_get(sp, name);
  	if (s) {
  		return s;
  	}
  
  	s = RZ_NEW0(RzSpace);
  	if (!s) {
  		return NULL;
  	}
  
  	s->name = strdup(name);
  	if (!s->name) {
  		free(s);
  		return NULL;
  	}
  
  	rz_rbtree_insert(&sp->spaces, s, &s->rb, space_cmp, NULL);
  	return s;
  }
  
  RZ_API RzSpace *rz_spaces_set(RzSpaces *sp, const char *name) {
  	sp->current = rz_spaces_add(sp, name);
  	return sp->current;
  }
  
  static inline bool spaces_unset_single(RzSpaces *sp, const char *name) {
  	RzSpace *space = rz_spaces_get(sp, name);
  	if (!space) {
  		return false;
  	}
  
  	RzSpaceEvent ev = { .data.unset.space = space };
  	rz_event_send(sp->event, RZ_SPACE_EVENT_UNSET, &ev);
  	if (sp->current == space) {
  		sp->current = NULL;
  	}
  	return rz_rbtree_delete(&sp->spaces, (void *)name, name_space_cmp, NULL, space_node_free, NULL);
  }
  
  RZ_API bool rz_spaces_unset(RzSpaces *sp, const char *name) {
  	if (name) {
  		return spaces_unset_single(sp, name);
  	}
  
  	RzList *names = rz_list_newf((RzListFree)free);
  	if (!names) {
  		return false;
  	}
  
  	RBIter it;
  	RzSpace *s;
  	rz_spaces_foreach(sp, it, s) {
  		rz_list_append(names, strdup(s->name));
  	}
  
  	RzListIter *lit;
  	const char *n;
  	bool res = false;
  	rz_list_foreach (names, lit, n) {
  		res |= spaces_unset_single(sp, n);
  	}
  	rz_list_free(names);
  	return res;
  }
  
  RZ_API int rz_spaces_count(RzSpaces *sp, const char *name) {
  	RzSpace *s = rz_spaces_get(sp, name);
  	if (!s) {
  		return 0;
  	}
  	RzSpaceEvent ev = { .data.count.space = s, .res = 0 };
  	rz_event_send(sp->event, RZ_SPACE_EVENT_COUNT, &ev);
  	return ev.res;
  }
  
  RZ_API bool rz_spaces_push(RzSpaces *sp, const char *name) {
  	rz_return_val_if_fail(sp, false);
  
  	rz_list_push(sp->spacestack, sp->current ? sp->current->name : "*");
  	rz_spaces_set(sp, name);
  	return true;
  }
  
  RZ_API bool rz_spaces_pop(RzSpaces *sp) {
  	char *name = rz_list_pop(sp->spacestack);
  	if (!name) {
  		return false;
  	}
  
  	RzSpace *s = rz_spaces_get(sp, name);
  	rz_spaces_set(sp, s ? s->name : NULL);
  	return true;
  }
  
  RZ_API bool rz_spaces_rename(RzSpaces *sp, const char *oname, const char *nname) {
  	if (!oname && !sp->current) {
  		return false;
  	}
  
  	RzSpace *s;
  	if (oname) {
  		s = rz_spaces_get(sp, oname);
  		if (!s) {
  			return false;
  		}
  	} else {
  		s = sp->current;
  	}
  
  	RzSpace *sn = rz_spaces_get(sp, nname);
  	if (sn) {
  		return false;
  	}
  
  	RzSpaceEvent ev = {
  		.data.rename.oldname = s->name,
  		.data.rename.newname = nname,
  		.data.rename.space = s
  	};
  	rz_event_send(sp->event, RZ_SPACE_EVENT_RENAME, &ev);
  
  	rz_rbtree_delete(&sp->spaces, (void *)s->name, name_space_cmp, NULL, NULL, NULL);
  	free(s->name);
  	s->name = strdup(nname);
  	rz_rbtree_insert(&sp->spaces, s, &s->rb, space_cmp, NULL);
  
  	return true;
  }
  