/*
 * Copyright (c) 2005-2023 Julien Nadeau Carriere <vedge@csoft.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * A subclass of AG_Combo(3) for selecting an element in a VFS of AG_Objects.
 */

#include <agar/core/core.h>
#if defined(AG_WIDGETS)

#include <agar/gui/objsel.h>
#include <agar/gui/window.h>
#include <agar/gui/primitive.h>
#include <agar/gui/label.h>
#include <agar/gui/iconmgr.h>

#include <stdarg.h>
#include <string.h>

AG_ObjectSelector *
AG_ObjectSelectorNew(void *parent, int flags, void *pobj, void *root,
    const char *fmt, ...)
{
	char *label;
	AG_ObjectSelector *os;
	va_list ap;

	os = Malloc(sizeof(AG_ObjectSelector));
	AG_ObjectInit(os, &agObjectSelectorClass);
	os->flags |= flags;
	os->pobj = pobj;
	os->root = root;

	va_start(ap, fmt);
	Vasprintf(&label, fmt, ap);
	va_end(ap);
	AG_TextboxSetLabelS(os->com.tbox, label);
	free(label);

	AG_ObjectAttach(parent, os);
	return (os);
}

/* Select an object in the list. */
AG_TlistItem *
AG_ObjectSelectorSelect(AG_ObjectSelector *os, void *p)
{
	return (AG_ComboSelectPointer(&os->com, p));
}

static void
FindObjects(AG_ObjectSelector *os, AG_Tlist *tl, AG_Object *pob, int depth)
{
	AG_Object *cob;
	AG_TlistItem *it;
	int nosel = 0;
	
	if (!AG_OfClass(pob, os->type_mask)) {
		if (!TAILQ_EMPTY(&pob->children)) {
			nosel++;
		} else {
			return;
		}
	}

	it = AG_TlistAddS(tl, NULL, pob->name);
	it->depth = depth;
	it->cat = "object";
	it->p1 = pob;
	if (nosel) {
		it->flags |= AG_TLIST_NO_SELECT;
	}
	if (!TAILQ_EMPTY(&pob->children)) {
		it->flags |= AG_TLIST_HAS_CHILDREN;
		if (pob->parent == NULL)
			it->flags |= AG_TLIST_ITEM_EXPANDED;
	}
	if ((it->flags & AG_TLIST_HAS_CHILDREN)) {
		TAILQ_FOREACH(cob, &pob->children, cobjs)
			FindObjects(os, tl, cob, depth+1);
	}
}

static void
ExpandObjects(AG_Event *event)
{
	AG_Tlist *tl = AG_TLIST_SELF();
	AG_ObjectSelector *os = AG_OBJECTSELECTOR_PTR(1);

	AG_ObjectLock(os);
	AG_LockVFS(os->root);

	FindObjects(os, tl, os->root, 0);

	AG_UnlockVFS(os->root);
	AG_ObjectUnlock(os);
}

static void
SelectObject(AG_Event *event)
{
	AG_ObjectSelector *os = AG_OBJECTSELECTOR_PTR(1);
	const AG_TlistItem *it = AG_TLISTITEM_PTR(2);
	AG_Variable *V;
	void **object;
	
	V = AG_GetVariable(os, "object", (void *)&object);
	*object = it->p1;
	AG_UnlockVariable(V);
}

static void
Bound(AG_Event *event)
{
	AG_ObjectSelector *os = AG_OBJECTSELECTOR_SELF();
	const AG_Variable *V = AG_PTR(1);

	if (strcmp(V->name, "object") == 0) {
		void **object = V->data.p;
	
		if (*object != NULL)
			AG_ComboSelectPointer(&os->com, *object);
	}
}

static void
Init(void *obj)
{
	AG_Combo *com = obj;
	AG_ObjectSelector *os = obj;

	WIDGET(os)->flags |= AG_WIDGET_HFILL;

	com->flags |= AG_COMBO_POLL;
	
	os->object = NULL;
	os->flags = 0;
	os->pobj = NULL;
	os->root = NULL;
	os->type_mask[0] = '*';
	os->type_mask[1] = '\0';

	AG_BindPointer(os, "object", &os->object);

	AG_SetEvent(&os->com, "combo-expand", ExpandObjects, "%p", os);
	AG_SetEvent(&os->com, "combo-selected", SelectObject, "%p", os);
	
	AG_SetEvent(os, "bound", Bound, NULL);
	OBJECT(os)->flags |= AG_OBJECT_BOUND_EVENTS;
}

void
AG_ObjectSelectorMaskType(AG_ObjectSelector *os, const char *type)
{
	AG_OBJECT_ISA(os, "AG_Widget:AG_Combo:AG_ObjectSelector:*");
	AG_ObjectLock(os);

	Strlcpy(os->type_mask, type, sizeof(os->type_mask));

	AG_ObjectUnlock(os);
}

AG_WidgetClass agObjectSelectorClass = {
	{
		"Agar(Widget:Combo:ObjectSelector)",
		sizeof(AG_ObjectSelector),
		{ 1,0, AGC_OBJECT_SELECTOR, 0xE063 },
		Init,
		NULL,		/* reset */
		NULL,		/* destroy */
		NULL,		/* load */
		NULL,		/* save */
		NULL		/* edit */
	},
	NULL,			/* draw */
	NULL,			/* size_request */
	NULL,			/* size_allocate */
	NULL,			/* mouse_button_down */
	NULL,			/* mouse_button_up */
	NULL,			/* mouse_motion */
	NULL,			/* key_down */
	NULL,			/* key_up */
	NULL,			/* touch */
	NULL,			/* ctrl */
	NULL			/* joy */
};

#endif /* AG_WIDGETS */
