/*	Public domain	*/

#ifndef _AGAR_WIDGET_TLIST_H_
#define _AGAR_WIDGET_TLIST_H_

#include <agar/gui/scrollbar.h>
#include <agar/gui/menu.h>
#include <agar/gui/label.h>
#include <agar/gui/begin.h>

#ifndef AG_TLIST_LABEL_MAX
# if AG_MODEL == AG_MEDIUM
#  define AG_TLIST_LABEL_MAX 252
# elif AG_MODEL == AG_LARGE
#  define AG_TLIST_LABEL_MAX 508
# endif
#endif

/* Popup menu (TODO switch to AG_PopupMenu) */
typedef struct ag_tlist_popup {
	const char *_Nonnull iclass;          /* Apply to items of this class */
	AG_Menu *_Nonnull menu;               /* The popup menu proper */
	AG_MenuItem *_Nonnull item;           /* Root of the popup menu */
	AG_Window *_Nullable panel;           /* Created by AG_MenuExpand() */
	AG_TAILQ_ENTRY(ag_tlist_popup) popups;
} AG_TlistPopup;

/* A tree/list item */
typedef struct ag_tlist_item {
	Uint32 tag;                      /* Tagged non-object */
	int label[3];                    /* Rendered item surface */
	                                 /* 0=Disabled, 1=Enabled, 2=Selected */
	int v;                           /* App-specific integer / sort key */
	const char *_Nullable cat;       /* App-specific category id */
	AG_Surface *_Nullable iconsrc;   /* Icon source image */
	void *_Nullable p1;              /* App-specific pointer */
	AG_Color *_Nullable color;       /* Alternate color */
	AG_Font *_Nullable font;         /* Alternate font */
	int selected;                    /* Effective selection flag */
	Uint depth;                      /* Indent in tree display */
	Uint flags;
#define AG_TLIST_ITEM_EXPANDED 0x01      /* Child items visible (tree) */
#define AG_TLIST_HAS_CHILDREN  0x02      /* Child items exist (tree) */
#define AG_TLIST_NO_SELECT     0x08      /* Item is not selectable */
#define AG_TLIST_NO_POPUP      0x10      /* Disable popups for item */
#define AG_TLIST_ITEM_DISABLED 0x20      /* Disable item (draw as disabled) */

	float scale;                     /* Text scaling factor */
	char text[AG_TLIST_LABEL_MAX];   /* Label text */
	Uint u;                          /* App-specific unsigned integer */

	AG_TAILQ_ENTRY(ag_tlist_item) items;    /* Items in list */
	AG_TAILQ_ENTRY(ag_tlist_item) selitems; /* Saved selection state */
} AG_TlistItem;

typedef AG_TAILQ_HEAD(ag_tlist_itemq, ag_tlist_item) AG_TlistItemQ;

typedef int (*AG_TlistCompareFn)(const AG_TlistItem *_Nonnull,
	                         const AG_TlistItem *_Nonnull);

/* Tree/list widget */
typedef struct ag_tlist {
	struct ag_widget wid;           /* AG_Widget -> AG_Tlist */
	Uint flags;
#define AG_TLIST_MULTI         0x0001      /* Multiple selections (ctrl/shift) */
#define AG_TLIST_MULTITOGGLE   0x0002      /* Multiple toggle-style selections */
#define AG_TLIST_POLL          0x0004      /* Generate tlist-poll events */
#define AG_TLIST_NO_SELECTED   0x0008      /* Inhibit "tlist-selected" event */
#define AG_TLIST_NO_SCALE_ICON 0x0010      /* Don't scale oversize icons. */
#define AG_TLIST_HFILL         0x0020
#define AG_TLIST_VFILL         0x0040
#define AG_TLIST_FIXED_HEIGHT  0x0080      /* Don't set icon height on "font-changed" */
#define AG_TLIST_STATELESS     0x0100      /* Don't preserve selection state (polled mode) */
#define AG_TLIST_SCROLLTOSEL   0x0200      /* Scroll to initial selection */
#define AG_TLIST_REFRESH       0x0400      /* Repopulate now (polled mode) */
#define AG_TLIST_EXPAND_NODES  0x0800      /* Expand node items (items with children) by default */
#define AG_TLIST_NO_KEYREPEAT  0x1000      /* Disable keyrepeat behavior */
#define AG_TLIST_NO_LINES      0x2000      /* Don't draw lines connecting items */
#define AG_TLIST_NO_BGLINES    0x4000      /* Don't draw lines in background */
#define AG_TLIST_EXPAND        (AG_TLIST_HFILL | AG_TLIST_VFILL)

	int item_h;                     /* Item height */
	void *_Nullable selected;       /* Default `selected' binding */
	int wHint, hHint;               /* Size hint */
	AG_Rect r;                      /* Clipping rectangle */
	int  *expLevels;                /* Tree expansion state for draw() */
	Uint nExpLevels;
	int icon_w;                     /* Item icon width */
	Uint pollDelay;                 /* Refresh rate for POLL mode */
	int rOffs;                      /* Row display offset */
	void *_Nullable dblClicked;     /* For double click test */
	AG_TlistItemQ items;            /* Current Items */
	AG_TlistItemQ selitems;         /* Saved item state */
	int nItems;                     /* Number of items total */
	int nVisible;                   /* Number of items on screen */
	AG_Scrollbar *_Nonnull sbar;    /* Vertical scrollbar */
	AG_TAILQ_HEAD_(ag_tlist_popup) popups; /* Popup menus */
	AG_TlistCompareFn compare_fn;   /* Item-item comparison function */
	AG_Event *_Nullable popupEv;    /* Popup menu hook */
	AG_Event *_Nullable changedEv;  /* Selection change hook */
	AG_Event *_Nullable dblClickEv; /* Double click hook */
	int lineScrollAmount;
	int lastKeyDown;                /* For key repeat */
	AG_Timer moveTo;                /* Timer for keyboard motion */
	AG_Timer refreshTo;             /* Timer for polled mode updates */
	AG_Timer dblClickTo;            /* Timer for detecting double clicks */
	AG_Timer ctrlMoveTo;            /* Timer for controller-driven move */
	AG_Color cBgLine[AG_WIDGET_NSTATES];  /* Background line color */
} AG_Tlist;

#define AGTLIST(o)            ((AG_Tlist *)(o))
#define AGCTLIST(o)           ((const AG_Tlist *)(o))
#define AG_TLIST_ISA(o)          (((AGOBJECT(o)->cid & 0xff000000) >> 24) == 0x28)
#define AG_TLIST_SELF()          AGTLIST( AG_OBJECT(0,"AG_Widget:AG_Tlist:*") )
#define AG_TLIST_PTR(n)          AGTLIST( AG_OBJECT((n),"AG_Widget:AG_Tlist:*") )
#define AG_TLIST_NAMED(n)        AGTLIST( AG_OBJECT_NAMED((n),"AG_Widget:AG_Tlist:*") )
#define AG_cTLIST_SELF()   AGCTLIST( AG_cOBJECT(0,"AG_Widget:AG_Tlist:*") )
#define AG_cTLIST_PTR(n)   AGCTLIST( AG_cOBJECT((n),"AG_Widget:AG_Tlist:*") )
#define AG_cTLIST_NAMED(n) AGCTLIST( AG_cOBJECT_NAMED((n),"AG_Widget:AG_Tlist:*") )

/* Tagged non-object */
#define    AGTLISTITEM(p)       ((AG_TlistItem *)(p))
#define   AGcTLISTITEM(p)       ((const AG_TlistItem *)(p))
#ifdef AG_TYPE_SAFETY
# define  AG_TLISTITEM_PTR(v)   AG_TlistGetItemPtr(event,(v),0)
# define AG_cTLISTITEM_PTR(v)   AGcTLISTITEM( AG_TlistGetItemPtr(event,(v),1) )
#else
# define  AG_TLISTITEM_PTR(v)   event->argv[v].data.p
# define AG_cTLISTITEM_PTR(v)   AGcTLISTITEM( event->argv[v].data.p )
#endif
#define  AG_TLISTITEM_SELF()    AG_TLISTITEM_PTR(0)
#define AG_cTLISTITEM_SELF()   AG_cTLISTITEM_PTR(0)

#define AG_TLIST_FOREACH(it, tl) AG_TAILQ_FOREACH(it, &(tl)->items, items)

#define AG_TLIST_FOREACH_ITEM(p, tl, it, t) for((it) = AG_TAILQ_FIRST(&(tl)->items), (p) = (it)!=NULL ? (struct t *)(it)->p1 : NULL; (it) != AG_TAILQ_END(&(tl)->children) && (it)->p1 != NULL; (it) = AG_TAILQ_NEXT((it), cobjs), (p) = (it)!=NULL ? (struct t *)(it)->p1 : NULL)

/* Begin generated block */
__BEGIN_DECLS
extern DECLSPEC AG_WidgetClass agTlistClass;
extern DECLSPEC AG_Tlist *_Nonnull AG_TlistNew(void *_Nullable, Uint);
extern DECLSPEC AG_Tlist *_Nonnull AG_TlistNewPolled(void *_Nullable, Uint, _Nonnull AG_EventFn, const char *_Nullable, ...);
extern DECLSPEC AG_Tlist *_Nonnull AG_TlistNewPolledMs(void *_Nullable, Uint, int, _Nonnull AG_EventFn, const char *_Nullable, ...);
extern DECLSPEC void AG_TlistSizeHint(AG_Tlist *_Nonnull, const char *_Nonnull, int);
extern DECLSPEC void AG_TlistSizeHintPixels(AG_Tlist *_Nonnull, int,int);
extern DECLSPEC void AG_TlistSizeHintLargest(AG_Tlist *_Nonnull, int);
extern DECLSPEC void AG_TlistSetItemHeight(AG_Tlist *_Nonnull, int);
extern DECLSPEC void AG_TlistSetIconWidth(AG_Tlist *_Nonnull, int);
extern DECLSPEC void AG_TlistSetRefresh(AG_Tlist *_Nonnull, int);
extern DECLSPEC void AG_TlistUniq(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistClear(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistRestore(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistBegin(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistEnd(AG_Tlist *_Nonnull);
extern DECLSPEC int AG_TlistVisibleChildren(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull);
#define AG_TlistClear(tl) AG_TlistBegin(tl)
#define AG_TlistRestore(tl) AG_TlistEnd(tl)
extern DECLSPEC AG_TlistItem *_Nonnull AG_TlistAddS(AG_Tlist *_Nonnull, const AG_Surface *_Nullable, const char *_Nonnull);
extern DECLSPEC AG_TlistItem *_Nonnull AG_TlistAdd(AG_Tlist *_Nonnull, const AG_Surface *_Nullable, const char *_Nonnull, ...) FORMAT_ATTRIBUTE(printf,3,4);
extern DECLSPEC AG_TlistItem *_Nonnull AG_TlistAddHeadS(AG_Tlist *_Nonnull, const AG_Surface *_Nullable, const char *_Nonnull);
extern DECLSPEC AG_TlistItem *_Nonnull AG_TlistAddHead(AG_Tlist *_Nonnull, const AG_Surface *_Nullable, const char *_Nonnull, ...) FORMAT_ATTRIBUTE(printf,3,4);
extern DECLSPEC AG_TlistItem *_Nonnull AG_TlistAddPtr(AG_Tlist *_Nonnull, const AG_Surface *_Nullable, const char *_Nonnull, void *_Nullable);
extern DECLSPEC AG_TlistItem *_Nonnull AG_TlistAddPtrHead(AG_Tlist *_Nonnull, const AG_Surface *_Nullable, const char *_Nonnull, void *_Nullable);
extern DECLSPEC void AG_TlistMoveToHead(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull);
extern DECLSPEC void AG_TlistMoveToTail(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull);
extern DECLSPEC AG_TlistItem *_Nonnull AG_TlistItemNew(const AG_Surface *_Nullable);
extern DECLSPEC void AG_TlistSetIcon(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull, const AG_Surface *_Nullable);
extern DECLSPEC void AG_TlistSetColor(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull, const AG_Color *_Nullable);
extern DECLSPEC void AG_TlistSetFont(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull, const char *_Nullable, float, Uint);
extern DECLSPEC void AG_TlistDel(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull);
extern DECLSPEC void AG_TlistSelect(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull);
extern DECLSPEC void AG_TlistDeselect(AG_Tlist *_Nonnull, AG_TlistItem *_Nonnull);
extern DECLSPEC void AG_TlistSelectIdx(AG_Tlist *_Nonnull, Uint);
extern DECLSPEC void AG_TlistDeselectIdx(AG_Tlist *_Nonnull, Uint);
extern DECLSPEC void AG_TlistSelectAll(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistDeselectAll(AG_Tlist *_Nonnull);
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistSelectPtr(AG_Tlist *_Nonnull, void *_Nullable);
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistSelectText(AG_Tlist *_Nonnull, const char *_Nonnull);
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistFindByIndex(AG_Tlist *_Nonnull, int) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistSelectedItem(AG_Tlist *_Nonnull) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC void *_Nullable AG_TlistSelectedItemPtr(AG_Tlist *_Nonnull) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistFirstItem(AG_Tlist *_Nonnull) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistLastItem(AG_Tlist *_Nonnull) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC void *_Nullable AG_TlistFindPtr(AG_Tlist *_Nonnull) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistFindText(AG_Tlist *_Nonnull, const char *_Nonnull) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC AG_MenuItem *_Nonnull AG_TlistSetPopup(AG_Tlist *_Nonnull, const char *_Nonnull);
extern DECLSPEC void AG_TlistScrollToSelection(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistScrollToStart(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistScrollToEnd(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistSetDblClickFn(AG_Tlist *_Nonnull, _Nonnull AG_EventFn, const char *_Nullable, ...);
extern DECLSPEC void AG_TlistSetPopupFn(AG_Tlist *_Nonnull, _Nonnull AG_EventFn, const char *_Nullable, ...);
extern DECLSPEC void AG_TlistSetChangedFn(AG_Tlist *_Nonnull, _Nonnull AG_EventFn, const char *_Nullable, ...);
extern DECLSPEC AG_TlistCompareFn AG_TlistSetCompareFn(AG_Tlist *_Nonnull, AG_TlistCompareFn);
extern DECLSPEC int AG_TlistCompareInts(const AG_TlistItem *_Nonnull, const AG_TlistItem *_Nonnull) _Pure_Attribute;
extern DECLSPEC int AG_TlistCompareIntsDsc(const AG_TlistItem *_Nonnull, const AG_TlistItem *_Nonnull) _Pure_Attribute;
extern DECLSPEC int AG_TlistCompareUints(const AG_TlistItem *_Nonnull, const AG_TlistItem *_Nonnull) _Pure_Attribute;
extern DECLSPEC int AG_TlistCompareStrings(const AG_TlistItem *_Nonnull, const AG_TlistItem *_Nonnull) _Pure_Attribute;
extern DECLSPEC int AG_TlistComparePtrs(const AG_TlistItem *_Nonnull, const AG_TlistItem *_Nonnull) _Pure_Attribute;
extern DECLSPEC int AG_TlistComparePtrsAndCats(const AG_TlistItem *_Nonnull, const AG_TlistItem *_Nonnull) _Pure_Attribute;
extern DECLSPEC void AG_TlistSort(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistSortByInt(AG_Tlist *_Nonnull);
extern DECLSPEC void AG_TlistRefresh(AG_Tlist *_Nonnull);
#ifdef AG_TYPE_SAFETY
extern DECLSPEC AG_TlistItem *_Nullable AG_TlistGetItemPtr(const AG_Event *_Nonnull, int, int);
#endif
#ifdef AG_LEGACY
#define AG_TLIST_ITEM_PTR(v) AG_TLISTITEM_PTR(v)
#define AG_TLIST_ITEM_SELF() AG_TLISTITEM_PTR(0)
#define AG_TLIST_TREE 0
#define AG_TLIST_EXPANDED AG_TLIST_ITEM_EXPANDED
#define AG_TLIST_VISIBLE_CHILDREN AG_TLIST_ITEM_EXPANDED
#define AG_TLIST_NOSELSTATE AG_TLIST_STATELESS
#define AG_TLIST_NOSELEVENT AG_TLIST_NO_SELECTED
#define AG_TlistPrescale(tl,text,n) AG_TlistSizeHint((tl),(text),(n))
#define AG_TlistComparePtrsAndClasses(a,b) AG_TlistComparePtrsAndCats(a,b)
#endif
__END_DECLS
/* Close generated block */

#include <agar/gui/close.h>
#endif /* _AGAR_WIDGET_TLIST_H_ */
