/*	Public domain	*/

#ifndef _AGAR_GUI_EDITABLE_H_
#define _AGAR_GUI_EDITABLE_H_

#include <agar/gui/widget.h>
#include <agar/gui/text.h>

#include <agar/gui/begin.h>

#if AG_MODEL == AG_LARGE
# define AG_EDITABLE_MAX 128            /* Built-in (default) buffer size */
#else
# define AG_EDITABLE_MAX 64
#endif

struct ag_window;
struct ag_popup_menu;

/* Working UCS-4 text buffer for internal use */
typedef struct ag_editable_buffer {
	AG_Variable *_Nullable var;	/* Variable binding (if any) */
	AG_Char *_Nullable s;		/* Buffer contents */
	AG_Size len;			/* String length (chars) */
	AG_Size maxLen;			/* Available buffer size (bytes) */
	int reallocable;		/* Buffer can be realloc'd */
	Uint32 _pad;
} AG_EditableBuffer;

/* Recorded modification for Undo/Redo */
typedef struct ag_editable_revision {
	Uint lenBuffer;                  /* Length of buffer (for Shared Access) */
	Uint32 crc32;                    /* CRC32 of buffer (for Shared Access) */
	int posStart;                    /* Start position (char index) */
	int posEnd;                      /* End position (char index) */
	int nCharsAdded;       	         /* Number of characters added */
	int nCharsRemoved;               /* Number of characters removed */
	AG_Char *_Nullable s;            /* Original buffer contents */
} AG_EditableRevision;

/* Internal clipboard for copy/paste and kill/yank */
typedef struct ag_editable_clipboard {
	_Nonnull_Mutex AG_Mutex lock;
	char encoding[32];		/* Character set encoding */
	AG_Char *_Nullable s;		/* Clipboard contents */
	AG_Size len;			/* Length in characters */
	AG_SIZE_PADDING(_pad);
} AG_EditableClipboard;

/* Autocomplete context */
typedef struct ag_autocomplete {
	AG_Timer to;                      /* Timer for delay after key-up */
	AG_Event *_Nonnull fn;            /* Callback routine & arguments */
	char winName[AG_OBJECT_NAME_MAX]; /* Name of expanded window (or "") */
	Uint32 _pad;
} AG_Autocomplete;

typedef struct ag_editable {
	struct ag_widget wid;              /* AG_Widget -> AG_Editable */
	Uint flags;
#define AG_EDITABLE_HFILL         0x0000001
#define AG_EDITABLE_VFILL         0x0000002
#define AG_EDITABLE_EXPAND       (AG_EDITABLE_HFILL | AG_EDITABLE_VFILL)
#define AG_EDITABLE_MULTILINE     0x0000004   /* Multiline edition */
#define AG_EDITABLE_BLINK_ON      0x0000008   /* Cursor blink state (internal) */
#define AG_EDITABLE_PASSWORD      0x0000010   /* Password (hidden) input */
#define AG_EDITABLE_ABANDON_FOCUS 0x0000020   /* Abandon focus on return */
#define AG_EDITABLE_INT_ONLY      0x0000040   /* Accepts only int input */
#define AG_EDITABLE_FLT_ONLY      0x0000080   /* Accepts only float input */
#define AG_EDITABLE_CATCH_TAB     0x0000100   /* Process tab key input */
#define AG_EDITABLE_CURSOR_MOVING 0x0000200   /* Cursor is being moved */
#define AG_EDITABLE_UPPERCASE     0x0000400   /* Render as uppercase */
#define AG_EDITABLE_KEEPVISCURSOR 0x0000800   /* Try to keep cursor visible */
#define AG_EDITABLE_LOWERCASE     0x0001000   /* Render as lowercase */
#define AG_EDITABLE_MARKPREF      0x0002000   /* Mark current cursor position */
#define AG_EDITABLE_EXCL          0x0004000   /* Exclusive Buffer Access */
#define AG_EDITABLE_NO_KILL_YANK  0x0008000   /* Disable [K]ill and [Y]ank functions */
#define AG_EDITABLE_RETURN_HELD   0x0010000   /* KEY_RETURN or KP_ENTER is held */
#define AG_EDITABLE_NO_ALT_LATIN1 0x0020000   /* Disable alt-key LATIN-1 mappings */
#define AG_EDITABLE_WORDWRAP      0x0040000   /* Word wrapping */
#define AG_EDITABLE_NOPOPUP	  0x0080000   /* Disable popup menu */
#define AG_EDITABLE_WORDSELECT	  0x0100000   /* Select whole words */
#define AG_EDITABLE_READONLY	  0x0200000   /* Disable user input */
#define AG_EDITABLE_MULTILINGUAL  0x0400000   /* Multilingual edition */
#define AG_EDITABLE_SHIFT_SELECT  0x0800000   /* Keyboard (shift) selection mode */
#define AG_EDITABLE_NUMERICAL     (AG_EDITABLE_INT_ONLY | AG_EDITABLE_FLT_ONLY)
#define AG_EDITABLE_NO_CLIPPING   0x1000000   /* Allow overflow */

	int lineScrollAmount;                /* Lines to scroll per event */
	const char *_Nonnull encoding;       /* Character set (default "UTF-8") */
	char text[AG_EDITABLE_MAX];          /* Small built-in text buffer */
	int hPre;                            /* Vertical size hint (# of lines) */
	int wPre;                            /* Horizontal size hint (px) */
	int pos;                             /* Cursor position (char index) */
	int selStart;                        /* Start of selection (char index) */
	int selEnd;                          /* End of selection (char index) */
	AG_Char compose;                     /* Last char entered in composition */
	int xCurs, yCurs;                    /* Last cursor position */
	int xSelStart, ySelStart;            /* Last selection start position */
	int xSelEnd, ySelEnd;                /* Last selection end position */
	int xCursPref;                       /* Requested cursor position */
	int x;                               /* Horizontal offset (px) */
	int xMax;                            /* Rightmost x of largest line (px) */
	int y;                               /* Vertical offset (lines) */
	int yMax;                            /* Lowest y (lines) */
	int yVis;                            /* Maximum visible area (lines) */
	int posKbdSel;                       /* Start of keyboard selection */
	Uint32 _pad;
	AG_EditableBuffer sBuf;              /* Working buffer (for Exclusive Access) */
	AG_Rect r;                           /* Clipping rectangle */
	AG_CursorArea *_Nullable ca;         /* Text cursor-change area */
	enum ag_language lang;               /* Selected language (for AG_Text) */
	int xScrollPx;                       /* Explicit scroll request in pixels */
	struct ag_popup_menu *_Nullable pm;  /* Right-click popup menu */
	int *_Nullable xScrollTo;            /* Scroll to that X-position */
	int *_Nullable yScrollTo;            /* Scroll to that Y-position */
	AG_Autocomplete *_Nullable complete; /* Autocomplete context */
	int fontMaxHeight;                   /* Maximum character height */
	int lineSkip;                        /* Y-increment in multiline mode */
	int suPlaceholder;                   /* Rendered "placeholder" text label */
	int selDblClick;                     /* Position of last double click (char index) */
	AG_Timer toRepeat;                   /* Key repeat timers (not direction keys) */
	AG_Timer toCursorBlink;              /* Cursor blink timer */
	AG_Timer toDblClick;                 /* Double click timer */
	AG_Timer toRepeatDirs[4];            /* Key repeat timers (UP DN R L) */
	Uint nUndo;                          /* Undo stack size */
	Uint nRedo;                          /* Redo stack size */
	AG_EditableRevision *_Nonnull undo;  /* Undo stack (History Buffer) */
	AG_EditableRevision *_Nonnull redo;  /* Redo stack */
} AG_Editable;

#define   AGEDITABLE(o)       ((AG_Editable *)(o))
#define  AGcEDITABLE(o)       ((const AG_Editable *)(o))
#define  AG_EDITABLE_ISA(o)   (((AGOBJECT(o)->cid & 0xff000000) >> 24) == 0x0E)
#define  AG_EDITABLE_SELF()    AGEDITABLE(   AG_OBJECT(0,        "AG_Widget:AG_Editable:*") )
#define  AG_EDITABLE_PTR(n)    AGEDITABLE(   AG_OBJECT((n),      "AG_Widget:AG_Editable:*") )
#define  AG_EDITABLE_NAMED(n)  AGEDITABLE(   AG_OBJECT_NAMED((n),"AG_Widget:AG_Editable:*") )
#define AG_cEDITABLE_SELF()   AGcEDITABLE( AG_cOBJECT(0,        "AG_Widget:AG_Editable:*") )
#define AG_cEDITABLE_PTR(n)   AGcEDITABLE( AG_cOBJECT((n),      "AG_Widget:AG_Editable:*") )
#define AG_cEDITABLE_NAMED(n) AGcEDITABLE( AG_cOBJECT_NAMED((n) "AG_Widget:AG_Editable:*") )

#define AGEDITABLE_IS_READONLY(ed) (((ed)->flags & AG_EDITABLE_READONLY) || AG_WidgetDisabled(ed))

/* Begin generated block */
__BEGIN_DECLS
extern DECLSPEC AG_WidgetClass agEditableClass;
extern DECLSPEC AG_EditableClipboard agEditableClipbrd;
extern DECLSPEC AG_EditableClipboard agEditableKillring;
extern DECLSPEC AG_Editable *_Nonnull AG_EditableNew(void *_Nullable , Uint);
extern DECLSPEC void AG_EditableBindASCII(AG_Editable *_Nonnull, char *_Nonnull, AG_Size);
#ifdef AG_UNICODE
extern DECLSPEC void AG_EditableBindUTF8(AG_Editable *_Nonnull, char *_Nonnull, AG_Size);
extern DECLSPEC void AG_EditableBindEncoded(AG_Editable *_Nonnull, const char *_Nonnull, char *_Nonnull, AG_Size);
extern DECLSPEC void AG_EditableBindText(AG_Editable *_Nonnull, AG_TextElement *_Nonnull);
extern DECLSPEC void AG_EditableSetLang(AG_Editable *_Nonnull, enum ag_language);
#else
# define AG_EditableBindUTF8(ed,b,s) AG_EditableBindASCII((ed),(b),(s))
#endif
extern DECLSPEC void AG_EditableAutocomplete(AG_Editable *_Nonnull, _Nullable AG_EventFn, const char *_Nullable, ...);
extern DECLSPEC void AG_EditableCloseAutocomplete(AG_Editable *_Nonnull);
extern DECLSPEC AG_EditableBuffer *_Nullable AG_EditableGetBuffer(AG_Editable *_Nonnull);
extern DECLSPEC void AG_EditableReleaseBuffer(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC void AG_EditableClearBuffer(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC int AG_EditableGrowBuffer(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, AG_Char *_Nonnull, AG_Size);
extern DECLSPEC void AG_EditableClearHistory(AG_Editable *_Nonnull);
extern DECLSPEC AG_EditableRevision *AG_EditableBeginRevision(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC void AG_EditableAbortRevision(AG_Editable *_Nonnull);
extern DECLSPEC void AG_EditableCommitRevision(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC void AG_EditableRevert(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, AG_EditableRevision *_Nonnull, AG_EditableRevision *_Nonnull, Uint);
extern DECLSPEC void AG_EditableUndo(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC void AG_EditableRedo(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC int AG_EditableCut(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, AG_EditableClipboard *_Nonnull, int);
extern DECLSPEC int AG_EditableCopy(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, AG_EditableClipboard *_Nonnull, int);
extern DECLSPEC void AG_EditableCopyChunk(AG_Editable *_Nonnull, AG_EditableClipboard *_Nonnull, AG_Char *_Nonnull, AG_Size);
extern DECLSPEC int AG_EditablePaste(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, AG_EditableClipboard *_Nonnull, int);
extern DECLSPEC int AG_EditableDelete(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC void AG_EditableSelectAll(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull);
extern DECLSPEC void AG_EditableSizeHint(AG_Editable *_Nonnull, const char *_Nonnull);
extern DECLSPEC void AG_EditableSizeHintPixels(AG_Editable *_Nonnull, Uint,Uint);
extern DECLSPEC void AG_EditableSizeHintLines(AG_Editable *_Nonnull, Uint);
extern DECLSPEC void AG_EditableSetPassword(AG_Editable *_Nonnull, int);
extern DECLSPEC void AG_EditableSetWordWrap(AG_Editable *_Nonnull, int);
extern DECLSPEC void AG_EditableSetExcl(AG_Editable *_Nonnull, int);
extern DECLSPEC void AG_EditableSetString(AG_Editable *_Nonnull, const char *_Nullable);
extern DECLSPEC void AG_EditableClearString(AG_Editable *_Nonnull);
extern DECLSPEC void AG_EditablePrintf(AG_Editable *_Nonnull, const char *_Nullable, ...);
extern DECLSPEC char *_Nonnull AG_EditableDupString(AG_Editable *_Nonnull);
extern DECLSPEC AG_Size AG_EditableCopyString(AG_Editable *_Nonnull, char *_Nonnull, AG_Size);
extern DECLSPEC int AG_EditableCatString(AG_Editable *_Nonnull, const char *_Nonnull, ...);
extern DECLSPEC int AG_EditableCatStringS(AG_Editable *_Nonnull, const char *_Nonnull);
extern DECLSPEC int AG_EditableInt(AG_Editable *_Nonnull);
extern DECLSPEC void AG_EditableSetIntOnly(AG_Editable *_Nonnull, int);
extern DECLSPEC float AG_EditableFlt(AG_Editable *_Nonnull);
extern DECLSPEC double AG_EditableDbl(AG_Editable *_Nonnull);
extern DECLSPEC void AG_EditableSetFltOnly(AG_Editable *_Nonnull, int);
extern DECLSPEC void AG_EditableInitClipboards(void);
extern DECLSPEC void AG_EditableDestroyClipboards(void);
extern DECLSPEC int AG_EditableMapPosition(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, int,int, int *_Nonnull);
extern DECLSPEC void AG_EditableMoveCursor(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, int,int);
extern DECLSPEC int AG_EditableGetCursorPos(const AG_Editable *_Nonnull) _Pure_Attribute;
extern DECLSPEC int AG_EditableSetCursorPos(AG_Editable *_Nonnull, AG_EditableBuffer *_Nonnull, int);
extern DECLSPEC int AG_EditableReadOnly(AG_Editable *_Nonnull) _Pure_Attribute_If_Unthreaded;
extern DECLSPEC void AG_EditableValidateSelection(AG_Editable *_Nonnull, const AG_EditableBuffer *_Nonnull);
#ifdef AG_LEGACY
# define AG_EDITABLE_NOEMACS AG_EDITABLE_NO_KILL_YANK
# define AG_EDITABLE_NOLATIN1 AG_EDITABLE_NO_ALT_LATIN1
# define AG_EditablePrescale(ed,s) AG_EditableSizeHint((ed),(s))
#endif
__END_DECLS
/* Close generated block */

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