/**
 * Generic window, keyboard and mouse management.
 * 
 * This modules provides very basic support for GUI management tailored for the
 * needs of the ACM program, and it is not intended for general use in other
 * applications. Implementation for both Microsoft Windows and X-Window (Unix,
 * Linux, Cygwin) is provided.
 * 
 * Geometric units are in pixels, with [0,0] being the coordinates of the
 * top-left corner and [W-1,H-1] being the coordinates of the bottom-right corner
 * of a window content area W pixels wide and H pixels tall.
 * 
 * A fixed color's look-up table (CLUT) is created and specific routines are
 * provided to map RGB values into the indeces of this CLUT that best match the
 * requested color.
 * 
 * For basic usage, first invoke gui_new() to create a new window.
 * Events are returned by gui_getEvent().
 * The only supported drawing routine is gui_drawLine() that draws a colored
 * segment.
 * The window and all the associated data structures can be released with
 * memory_dispose().
 * 
 * @file
 * @author Umberto Salsi <salsi@icosaedro.it>
 * @version $Date: 2017/09/09 21:58:57 $
 */

#ifndef GUI_H
#define GUI_H

#ifdef gui_IMPORT
	#define EXTERN
#else
	#define EXTERN extern
#endif

#define gui_KEY_INS  (-1)
#define gui_KEY_HOME  (-2)
#define gui_KEY_END  (-3)
#define gui_KEY_PGUP  (-4)
#define gui_KEY_PGDW  (-5)
#define gui_KEY_DEL  (-7)
#define gui_KEY_UP  (-8)
#define gui_KEY_DOWN  (-9)
#define gui_KEY_LEFT  (-10)
#define gui_KEY_RIGHT  (-11)
#define gui_KEY_PAD_PLUS  (-12)
#define gui_KEY_PAD_MINUS  (-13)
#define gui_KEY_PAD_TIMES  (-14)
#define gui_KEY_PAD_DIVIDE  (-15)
#define gui_KEY_PAD_0  (-16)
#define gui_KEY_PAD_1  (-17)
#define gui_KEY_PAD_2  (-18)
#define gui_KEY_PAD_3  (-19)
#define gui_KEY_PAD_4  (-20)
#define gui_KEY_PAD_5  (-21)
#define gui_KEY_PAD_6  (-22)
#define gui_KEY_PAD_7  (-23)
#define gui_KEY_PAD_8  (-24)
#define gui_KEY_PAD_9  (-24)
#define gui_KEY_F(N) (-24 - (N))

/**
 * Single allocated window.
 */
typedef struct gui_Type gui_Type;

/**
 * Estimated dimensions of the "main" or "desktop" display are
 * returned in this data structure as pixels and millimiters.
 */
typedef struct {
	int heightPixels, heightMillimiters, widthPixels, widthMillimiters;
} gui_DisplayDimensions;

/**
 * Event codes that may be returned.
 */
typedef enum {
	// Left, Middle and Right mouse button down and up.
	gui_EVENT_LBUTTONDOWN,
	gui_EVENT_MBUTTONDOWN,
	gui_EVENT_RBUTTONDOWN,
	gui_EVENT_LBUTTONUP,
	gui_EVENT_MBUTTONUP,
	gui_EVENT_RBUTTONUP,
	
	/** Keyboard key pressed. */
	gui_EVENT_KEYDOWN,
			
	// Windows focus in and out. Brand new windows have no focus, then the
	// focus in event is one of the early events generated.
	gui_EVENT_WINDOW_FOCUS_IN,
	gui_EVENT_WINDOW_FOCUS_OUT,
	
	/** Part of the content area need to be redrawn. */
	gui_EVENT_WINDOW_EXPOSE,
	
	/** Window size changed. */
	gui_EVENT_WINDOW_SIZE_CHANGE,
	
	/** Windows close request. */
	gui_EVENT_WINDOW_CLOSE
} gui_EventCode;

/**
 * Event.
 */
typedef struct {
	
	/** Code of the event. */
	gui_EventCode code;
	
	/**
	 * For mouse button down/up events, these are the pointer's coordinates.
	 * For window size changes, these are the new width and height.
	 */
	int x, y;
	
	/**
	 * For key down event, this is the key code. ASCII codes are returned as
	 * usual values; for function keys, see the gui_KEY_F(N) macro. Other
	 * locale key codes might be returned, but these are not used by ACM and
	 * should be ignored.
	 */
	int key;
	
} gui_Event;

/**
 * Creates a window.
 * @param title Title for the title bar.
 * @param width Width of the content area (pixels).
 * @param height Height of the content area (pixels).
 * @return New window. Must be released with memory_dispose().
 */
EXTERN gui_Type * gui_new(char *title, int width, int height);

/**
 * Retrieves the dimensions of the current "main" or "desktop" display. On some
 * systems and some configurations the result might be completely wrong or
 * simply cannot be determined and a default is returned instead.
 * @param this Involved window.
 * @param d Here returns the dimensions.
 */
EXTERN void gui_getDisplayDimensions(gui_Type *this, gui_DisplayDimensions *d);

/**
 * Returns the next event. This function is not blocking, and returns false if
 * no meaningful event is available.
 * @param this Involved window.
 * @param e Here set the event.
 * @return True if a meaningful event has been set.
 */
EXTERN int gui_getEvent(gui_Type *this, gui_Event *e);

/**
 * Get the latest know position of the mouse pointer in the content area of the
 * window.
 * @param this Involved window.
 * @param x
 * @param y
 */
EXTERN void gui_getPointerPosition(gui_Type *this, int *x, int *y);

/**
 * Returns window width.
 * @param this Involved window.
 * @return Window width (pixels).
 */
EXTERN int gui_getWidth(gui_Type *this);

/**
 * Returns window height.
 * @param this Involved window.
 * @return Window height (pixels).
 */
EXTERN int gui_getHeight(gui_Type *this);

/**
 * Returns true if the window currently has input focus. When the focus is lost,
 * the client should ignore any user's input, either from the mouse or the
 * keyboard.
 * @param this Involved window.
 * @return True if the window currently has input focus.
 */
EXTERN int gui_hasFocus(gui_Type *this);

/**
 * Returns true if the content area of the window is (at least partially) visible
 * and then it worths to update its content. Returns false if the content area is
 * not visible, for example because the window has been minimized or iconified
 * (whatever these terms means for the specific underlying windowing system) so
 * the client can save time avoiding to update the content area.
 * @param this Involved window.
 * @return True if the content area of the window is (at least partially) visible.
 */
EXTERN int gui_isVisible(gui_Type *this);

/**
 * Returns the index to the CLUT that better matches the specified RGB.
 * @param this Involved window.
 * @param red Red value in [0,255].
 * @param green Green value in [0,255].
 * @param blue Blue value in [0,255].
 * @return Index into the table of the allocated colors for this window.
 */
EXTERN int gui_getColorIndex(gui_Type *this, int red, int green, int blue);

/**
 * Returns the index to the CLUT that better matches the specified
 * descriptive color string.
 * @param this Involved window.
 * @param s Descriptive string of the color name (example: "red") or hexadecimal
 * RGB value (examples: "#f00", "#ff0000").
 * @return Index into the table of the allocated colors for this window.
 */
EXTERN int gui_getColorIndexString(gui_Type *this, char *s);

/**
 * Parses the descriptive color string and returns the corresponding RGB value.
 * @param s Descriptive string of the color name (example: "red") or hexadecimal
 * RGB value (examples: "#f00", "#ff0000").
 * @param red Here returns the red value in [0,255].
 * @param green Here returns the green value in [0,255].
 * @param blue Here returns the blue value in [0,255].
 * @return True if the descriptive color string has been successfully parsed.
 */
EXTERN int gui_parseColorString(char *s, int *red, int *green, int *blue);

/**
 * Draws a segment of line on the window. The segment is 1 pixel wide.
 * Ending points can be specified in any order, resulting in the same exact
 * result being drawn. For example, the segment (1,1)-(2,1) causes two pixels
 * being drawn. Note that actually ACM only needs to draw horizontal segments,
 * so this function is just a bit more general than required.
 * 
 * Under X-Window a great performance improvement can be achieved by grouping
 * lines having the same color, as this reduces the need to change the graphic
 * context. This grouping is exactly what the Alib module of ACM does right now.
 * 
 * @param this Involved window.
 * @param x1
 * @param y1
 * @param x2
 * @param y2
 * @param color Color index as returned by gui_getColorIndex().
 */
EXTERN void gui_drawLine(gui_Type *this, int x1, int y1, int x2, int y2, int color);

#undef EXTERN
#endif
