/*
   ui/widget.h
   This file is part of the Osirion project and is distributed under
   the terms of the GNU General Public License version 2
*/

#ifndef __INCLUDED_UI_WIDGET_H__
#define __INCLUDED_UI_WIDGET_H__

#include "SDL2/SDL.h"

#include <string>
#include <list>

#include "auxiliary/functions.h"
#include "math/color.h"
#include "math/vector2f.h"
#include "ui/font.h"
#include "ui/palette.h"
#include "ui/definitions.h"
#include "sys/sys.h"

namespace ui
{

class Widget
{

public:
	/// type definition for child widgets
	typedef std::list<Widget *> Children;


	/// types of custom events a widget can emit
	enum Event {
		EventNone = 0, 
		EventButtonClicked = 1, 
		EventListItemClicked = 2, 
		EventListViewChanged = 3, 
		EventSliderChanged = 4,
		EventScrollBarChanged = 5,
		EventWindowShow = 6,
		EventWindowHide = 7,
		EventClicked = 8,
		EventDoubleClicked = 9
	};
	
	/// create a new widget
	Widget(Widget *parent = 0);

	/// destroy a widget
	virtual ~Widget();

	/// parent widget this widget belongs to
	inline Widget *parent() const {
		return widget_parent;
	}

	/* -- inspectors -------------------------------------------- */

	/// child widgets
	inline Children &children() {
		return widget_children;
	}



	/// pixel coordinates of the top-left corner of this widget within its parent
	inline const math::Vector2f &location() const {
		return widget_location;
	}

	/// size of this widget in pixels
	inline const math::Vector2f &size() const {
		return widget_size;
	}

	/// x coordinate of the left of the widget
	/**
	 * @see location()
	 */
	inline const float left() const {
		return widget_location.x();
	}

	/// x coordinate of the right of the widget
	/**
	 * @see location()
	 * @see size()
	 */
	inline const float right() const {
		return widget_location.x() + widget_size.width();
	}

	/// y coordinate of the top of the widget
	/**
	 *  @see location()
	 */
	inline const float top() const {
		return widget_location.y();
	}

	/// y coordinate of the bottom of the widget
	/**
	 * @see location()
	 * @see size()
	 */
	inline const float bottom() const {
		return widget_location.y() + widget_size.height();
	}

	/// width of the widget in pixels
	/**
	 *  @see size()
	 */
	inline const float width() const {
		return widget_size.width();
	}

	/// height of the widget in pixels
	/**
	 *  @see size()
	 */
	inline const float height() const {
		return widget_size.height();
	}

	/// widget label
	inline const std::string &label() const {
		return widget_label;
	}

	/// true if this widget will draw a background
	inline const bool background() const {
		return widget_background;
	}

	/// true if this widget will draw a border
	inline const bool border() const {
		return widget_border;
	}

	/// true if this widget is visible
	inline const bool visible() const {
		return widget_visible;
	}

	/// true if this widget is not visible
	inline const bool hidden() const {
		return !widget_visible;
	}

	/// true if the widget is enabled
	inline const bool enabled() const {
		return widget_enabled;
	}
	
	/// true if the widget is disabled
	inline const bool disabled() const {
		return !widget_enabled;
	}
	
	/// the palette used to draw this widget
	const Palette *palette() const;

	/// the font used to draw this widget
	const Font *font() const;

	/// return true if the widget has input focus
	bool has_input_focus() const;

	/// returns true if the widget has mouse focus
	bool has_mouse_focus() const;
	
	/// return true of the widget is a child widget
	bool is_child(const Widget *widget) const;

	/// return the next sibling
	Widget *next_sibling();


	/* -- mutators --------------------------------------------- */

	/// raise the widget to the top of the widget stack
	void raise();

	/// lower the widget to the bottom of the widget stack
	void lower();

	/// show the widget
	virtual void show();

	/// hide the widget
	virtual void hide();
	
	/// set visibility
	void set_visible(const bool visible = true);

	/// enable or disable the widget
	virtual void enable();

	/// enable or disable the widget
	virtual void disable();

	///set enabled or disabled state
	void set_enabled(const bool enabled = true);

	/// set input focus
	void set_focus();

	/// set the widget geometry
	void set_geometry(const float x, const float y, const float w, const float h);

	/// set the widget geometry
	void set_geometry(const math::Vector2f &location, const math::Vector2f &size);

	/// set location of the top-left corner, relative to the parent
	void set_location(const float x, const float y);

	/// set location of the top-left corner, relative to the parent
	void set_location(const math::Vector2f &location);

	/// set the widgets width and height
	void set_size(const float w, const float h);

	/// set the widgets width and height
	void set_size(const math::Vector2f &size);

	/// set the widgets width
	void set_width(const float w);

	/// set the widgets height
	void set_height(const float h);

	/// set the widgets palette
	void set_palette(const Palette *palette);

	/// set the widgets font
	void set_font(const Font *font);

	/// set the widgets label
	void set_label(const std::string &label);

	/// set the widgets label
	void set_label(const char *label);

	/// enable or disable widget border
	void set_border(const bool border = true);

	/// enable or disable widget background
	void set_background(const bool background = true);
	
	/* -- event distributors --------------------------------------- */

	/**
	 * @brief calls the resize event handler and sends the event to all child widgets
	 * @see resize
	 **/
	void event_resize();
	
	/**
	 * @brief calls the draw event handler and sends the event to all child widgets
	 * @see draw
	 **/
	void event_draw();

	/**
	 * @brief calls the key event handlers and sends unhandled keys to the parent widget
	 * @see on_keypress
	 * @see on_keyrelease
	 **/
	bool event_key(const bool pressed, const int key, const unsigned int modifier);
	
	/**
	 * @brief calls the on_mousepress and on_mouserelease event handlers and sends undhandled events to the parent widget
	 * @see on_mousepress
	 * @see on_mouserelease
	 * */
	bool event_mouse_button(const bool pressed, const unsigned int button);
	
	/**
	 * @brief calls the on_mousewheel event handlers and sends unhandled events to the parent widget
	 * @see on_mousewheel
	 * */
	bool event_mouse_wheel(const math::Vector2f & direction);

	/**
	 * @brief calls the mouse event handlers and sends unhandled keys to the parent widget
	 * @see on_mousemove
	 * @see on_mouseover
	 **/
	void event_mouse(const math::Vector2f & cursor);
	
	/**
	 * @brief calls the custom event handler and sends unhandled events to the parent widget
	 * @see on_emit
	 **/
	bool event_emit(Widget *sender, const Event event, void *data = 0);

	/**
	 * @brief emit a custom event
	 * */
	inline void emit(const Event event, void *data=0) {
		event_emit(this, event, data);
	}

protected:
		
	/// find the widget that has input focus
	virtual Widget *find_input_focus();

	/// find widget that has mouse focus
	/** @param cursor mouse cursor position relative to this widget's location
	*/
	Widget *find_mouse_focus(const math::Vector2f & cursor);
	
	/// find a visible widget 
	Widget *find_visible_child(const Widget *widget);

	/// list widget content
	size_t list(const size_t indent,  const bool visible_only = false) const;

	/// print widget description
	virtual void print(const size_t indent) const;

	/// true of this sibling has local focus
	inline bool focus() const {
		return widget_focus;
	}

	/* -- coordinate mapping ----------------------------------- */

	/// map local widget location to global location
	inline math::Vector2f global_location() {
		math::Vector2f v(widget_location);
		Widget *parent = widget_parent;
		while (parent) {
			v += parent->location();
			parent = parent->parent();
		}
		return v;
	}

	/// map local coordinates to global coordinates
	inline math::Vector2f to_global_coords(const math::Vector2f &local) {
		math::Vector2f v(local);
		Widget *parent = widget_parent;
		do {
			v += parent->location();
			parent = parent->parent();
		} while (parent);
		return v;
	}

	/// map global coordinates to local coordinates
	inline math::Vector2f to_local_coords(const math::Vector2f &global) {
		math::Vector2f v(global);
		Widget *parent = this;
		while (parent) {
			v -= parent->location();
			parent = parent->parent();
		}
		return v;
	}

	/* -- event handlers --------------------------------------- */

	/// called when the mouse receives mouse movement
	virtual void on_mousemove(const math::Vector2f &cursor);

	/// called when the mouse enters the widget
	virtual void on_mouseover(const math::Vector2f &cursor);
	
	/// called when a mouse button is pressed
	virtual bool on_mousepress(const unsigned int button);

	/// called when a mouse button is released
	virtual bool on_mouserelease(const unsigned int button);

	/// called when the scrollwheel is used
	virtual bool on_mousewheel(const math::Vector2f & direction);

	/// called when the widget receives a key press
	virtual bool on_keypress(const int key, const unsigned int modifier);

	/// called when the widget receives a key release
	virtual bool on_keyrelease(const int key, const unsigned int modifier);
	
	/// called when the widget receives a custom event
	virtual bool on_emit(Widget *sender, const Event event, void *data=0);

	/* -- draw functions --------------------------------------- */

	/// resize event
	virtual void resize();

	/// draw the widget
	virtual void draw();

	/// draw the widget background
	virtual void draw_background();

	/// draw the widget border
	virtual void draw_border();
	
	/// draw denug state
	virtual void draw_debug();

	/// add a child widget
	virtual void add_child(Widget *child);

	/// remove a child widget
	virtual void remove_child(Widget *child);
	
	/// remove all child widgets
	virtual void remove_children();


private:

	bool			widget_visible;
	bool			widget_background;
	bool			widget_border;
	bool			widget_focus;
	bool			widget_enabled;

	math::Vector2f		widget_location;
	math::Vector2f		widget_size;
	std::string		widget_label;

	Children		 widget_children;

	const Palette		*widget_palette;
	const Font		*widget_font;
	Widget			*widget_parent;

	Children::iterator find_child(Widget *child);

};

}

#endif // __INCLUDED_UI_WIDGET_H__