// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program is distributed in the hope that it will be useful, but
//   WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#ifndef WIDGET_H
#define WIDGET_H

#include <boost/bind.hpp>
#include <boost/function.hpp>

#include <vector>
#include <string>
#include <map>

#include "../image/image.h"
#include "../math/matrix.h"
#include "../math/vector2d.h"

#include "autogen.h"

typedef struct ImageTexture ImageTexture;
typedef class TiXmlElement TiXmlElement;
typedef struct lua_State lua_State;
typedef union SDL_Event SDL_Event;

// If you update this, make sure you update widgetGetStringForEvent()
// to go with it.
enum WidgetEventType {
	WIDGETEVENT_IDUNNO,
	
	// WidgetEvent_Mouse
	WIDGETEVENT_MOUSEMOTION,	// Mouse moved.
	WIDGETEVENT_MOUSEDOWN,		// Mouse button pressed.
	WIDGETEVENT_MOUSEUP,		// Mouse button released.
	
	// WidgetEvent_Keyboard
	WIDGETEVENT_KEYDOWN,		// Key pressed.
	WIDGETEVENT_KEYREPEAT,		// For repeat events when a key is held.
	WIDGETEVENT_KEYUP,			// Key released.
	
	// WidgetEvent_Focus
	WIDGETEVENT_FOCUSENTER,		// Focus has entered this Widget.
	WIDGETEVENT_FOCUSLEAVE,		// Focus is leaving this Widget.
	
	// Containers will actually have to move to the next widget from
	// what it has focused for these.
	WIDGETEVENT_FOCUSNEXT,		// Command to switch focus to the next Widget.
	WIDGETEVENT_FOCUSPREV,		// Command to switch focus to the previous Widget.
	
	// To accept focus, a widget should return true from this.
	WIDGETEVENT_FOCUSACCEPT,	// A Widget should return true from this to indicate that it is focusable, or succeeded in getting some child to focus.
	WIDGETEVENT_FOCUSACCEPT_BACK, // Same as WIDGETEVENT_FOCUSACCEPT, but tells the containers to go in reverse order.
	
	// TODO: Joystick events.
};

enum WidgetEvent_Key {
	
	// Must be first, for default. (So we can memset WidgetEvents to 0
	// and have this be automatic.) For when there was an issue
	// figuring out what key was actually pressed.
	WIDGET_KEY_IDUNNO,
	
	WIDGET_KEY_LEFT,
	WIDGET_KEY_RIGHT,
	WIDGET_KEY_UP,
	WIDGET_KEY_DOWN,
	WIDGET_KEY_HOME,
	WIDGET_KEY_END,
	
	WIDGET_KEY_SPACE,
	WIDGET_KEY_RETURN,
	
	WIDGET_KEY_PAGEUP,
	WIDGET_KEY_PAGEDOWN,

    WIDGET_KEY_ESCAPE,
};

struct WidgetEvent_Focus {
	WidgetEventType type;
};

struct WidgetEvent_Mouse {
	WidgetEventType type;
	float x;
	float y;
	int button;
};

struct WidgetEvent_Keyboard {
	WidgetEventType type;
	
	/// ASCII value for the key.
	char convertedKey;
	
	/// Corresponds to WidgetEvent_Key.
	int keyCode;

	// Modifiers...

	/// Shift held?
	bool shift;
	
	/// Alt held?
	bool alt;
	
	/// Ctrl held?
	bool ctrl;
};

/// Union of all the event types.
union WidgetEvent {
	WidgetEventType type;
	
	WidgetEvent_Mouse mouse;
	WidgetEvent_Keyboard keyboard;
	WidgetEvent_Focus focus;
};

/// Set to the maximum number of mouse buttons we'll worry about. Note
/// that the number pad on the side of a Razer Naga actually sends
/// keyboard events and we don't have to worry about those. :P
#define WIDGET_NUMMOUSEBUTTONS 8

/// If this is defined, some Widgets that do operations likely to
/// result in a position or size with a fractional part like
/// WidgetAlignedBox and WidgetButton (when centering stuff) will try
/// to snap the result to the nearest whole pixel. Fixes font
/// blurrienss.
#define WIDGET_SNAP_TO_PIXELS

typedef bool (*WidgetCallback)(Widget *, void *);

// TODO: Add all the events to the description here.

/// Base Wigdet class. This is the root of the entire Widget hierarchy
/// and actually does quite a lot. The event handling system, size, and
/// basic drawing capability (just the background) are all handled here.
LUA_WRAP class Widget {
public:
	
    // Constructor/destructor.
    Widget(void);
    Widget(TiXmlElement *xml);
    virtual ~Widget(void);
		
    /// Set the width of the widget. This is enforced by parent
    /// widgets and any manual setting with it will probably get
    /// clobbered unless it's a root Widget (not child to
    /// anything.) Will not set the size below the minimum. See
    /// setMinWidth().
    LUA_WRAP virtual void setWidth(float newWidth);
		
    /// Set the width of the Widget. Same conditions as setWidth().
    LUA_WRAP virtual void setHeight(float newHeight);
		
    /// Get the current width.
    LUA_WRAP float getWidth(void);
		
    /// Get the current height.
    LUA_WRAP float getHeight(void);
		
    /// Draw the widget. The base version of this does some drawing
    /// that most inherited widgets will want to to anyway.
    /// (Ninepatch and background.)
    virtual void draw(void);
		
    /// Set the minimum height value. Calculated sizes (like the
    /// cumulative size of child widgets) can still exceed this, in
    /// which case that value will be used instead for resizing.
    LUA_WRAP virtual void setMinHeight(float minHeight);
		
    /// Set minimum width. Same conditions as setMinHeight().
    LUA_WRAP virtual void setMinWidth(float minWidth);
		
    /// Get the minimum height. This INCLUDES calculated values,
    /// and is not necessarily the same as was passed into
    /// setMinHeight().
    LUA_WRAP virtual float getMinHeight(void);
		
    /// Get the minimum width. Same conditions as getMinHeight().
    LUA_WRAP virtual float getMinWidth(void);
		
    /// Set the background image to be the image in a given file.
    LUA_WRAP virtual void setBackgroundImage(const std::string &fileName);
		
    /// Get the background image. Note that this won't be the same
    /// as what went into setBackgroundImage() if a ninepatch was
    /// set then.
    LUA_WRAP std::string getBackgroundImage(void) const;
		
    // TODO: Make this private.
    /// Name of the Widget.
    std::string name;
	
    /// Handle an event. Returns true if it "ate" the event and
    /// nothing else should handle it.
    virtual bool handleEvent(const WidgetEvent *event);
	
    /// Returns true if this Widget still needs mouse events, even
    /// if the mouse isn't over it. (Like if the mouse button was
    /// pressed on it, then dragged away before release - it still
    /// wants that release event.)
    LUA_WRAP virtual bool needsMouseEvents(void);
	
    /// This is so we can send mouse exit events, even though they're
    /// outside the widget.
    bool mouseWasOverMe;
		
    /// So we can send mouse up events, even if it's outside the
    /// widget.
    bool mouseButtonsDownOnMe[WIDGET_NUMMOUSEBUTTONS];
		
    /// Get the value of sizeDirty, or any children's size dirty bit.
    LUA_WRAP virtual bool isSizeDirty(void);
		
    /// Mark this widget as needing its parents to resize.
    LUA_WRAP virtual void markSizeDirty(void);
		
    /// Get the UID/reference for this.
    LUA_WRAP const std::string &getUID(void);
		
    /// Set Lua code to run on an event.
    LUA_WRAP void setLuaEventHandler(const std::string &event, const std::string &luaCode);
		
    /// Get the Lua code that was set with setLuaEventHandler().
    LUA_WRAP std::string getLuaEventHandler(const std::string &event);
		
    /// Set a native event handler callback. Please don't use this
    /// anymore. It's horrible and deprecated!
    void setNativeEventHandler(const std::string &event, WidgetCallback func, void *param);
		
    /// Clear a native event handler callback.
    void clearNativeEventHandler(const std::string &event);
	
    /// Add a native event handler callback (new method). Please
    /// please please use this instead of setNativeEventHandler().
    void addNativeBindEventHandler(const std::string &event, boost::function<bool()> handler, bool addToFrontOfList = false);

    /// Find a child Widgets (this included) that has a given name.
    LUA_WRAP virtual Widget *findChildByName(const std::string &name);
		
    /// Set the name of this Widget.
    LUA_WRAP virtual void setName(const std::string &newName);
		
    /// Get the name of this Widget.
    LUA_WRAP std::string getName(void);
		
    /// Get the parent Widget.
    LUA_WRAP Widget *getParent(void) const;
		
    // FIXME: Why isn't this private or protected?
    /// Whatever widget contains this Widget.
    Widget *parent;

    /// Set background texture UV offset.
    LUA_WRAP void setBackgroundOffset(float x, float y);

private:
	
    // TODO: Remove these. Replace with scaling background or
    //   just specifying a Rect of it.
		
    /// Horizontal offset to the background drawing. (Deprecated.)
    float bgOffsetX;
    /// Vertical offset to background drawing. (Deprecated.)
    float bgOffsetY;
		
    /// Index in the main TextureManager for the given background.
    int backgroundTextureIndex;
	
    /// Unique identifier and reference for getting it without
    /// the danger of dangling pointer dereferences.
    std::string uid;
	
protected:
	
    /// Whether the widget thinks it has focus or not (based on
    /// focus events handled in Widget::handleEvent()).
    bool hasFocus;
	
    /// Needs an update for size.
    bool sizeDirty;
	
    /// Quick test to make sure a mouse event actually takes place
    /// within a widget's bounding box.
    inline bool mouseEventTouchesMe(const WidgetEvent_Mouse *mouseEvent) {
        return (mouseEvent->x >= 0 && mouseEvent->y >= 0 && mouseEvent->x <= width && mouseEvent->y <= height);
    }
	
    /// The currently set width. This can change often and is flexible.
    float width;
		
    /// The currently set height. This can change often and is flexible.
    float height;
		
    /// Minimum width of the widget. Arbitrary size that the widget
    /// will refuse to go smaller than, even if it can.
    float minWidth;
		
    /// Minimum height.
    float minHeight;
		
    struct WidgetRect {
        float x;
        float y;
        float w;
        float h;
    };
		
    struct WidgetNinePatch {
        WidgetRect outerBox; // Note: Setting x,y for this doesn't do anything. Leave them at 0.
        WidgetRect innerBox;
    };
		
    /// Ninepatch information.
    WidgetNinePatch *ninePatch;
		
    struct WidgetNativeEventHandler {
        WidgetCallback func;
        void *param;
    };

    /// Maps WidgetEventTypes to native C/C++ events.
    std::map<std::string, WidgetNativeEventHandler *> nativeEventHandlers;

    /// Maps WidgetEventTypes to native stuff setup with boost::bind
    std::map<std::string, std::vector< boost::function<bool()> > > nativeBindEventHandlers;

    /// Maps WidgetEventTypes to Lua code. (Lua source code goes
    /// right into the std::string objects.)
    std::map<std::string, std::string> luaEventHandlers;
		
    /// Handle Lua events. Usually called from handleEvent(). Just
    /// looks up the eventName in luaEventHandlers and runs it.
    /// FIXME: Name is misleading. Handles native events too.
    bool handleLuaEvent(const std::string &eventName, const WidgetEvent *originalEvent);
		
    // Static stuff.
		
    /// Push a clipping area on the stencil buffer like a stack.
    static void pushClipArea(float x, float y, float w, float h);
		
    /// Undraw the last clipping area, effectively popping it from
    /// the stack of clipping areas.
    static void popClipArea(void);
	
#   ifdef WIDGET_CLIP_AREA_TEST
    // Just temporary. Draw clipping areas.
    static void dumpClipAreas(void);
#   endif

    /// All the previous clipping areas.
    static std::vector<WidgetRect*> clippingStack;

#   ifdef WIDGET_CLIP_AREA_TEST
    static std::vector<Widget::WidgetRect*> recordedClippingAreas;
#   endif
		
    /// Clipboard set.
    static void setClipboard(const std::string &clipBoard);
		
    /// Clipboard get.
    static std::string getClipboard(void);
		
    /// The clipboard.
    static std::string clipboardText;
		
    /// Check to see if another Widget is this Widget's parent
    /// somewhere possibly far up the hierarchy.
    bool widgetIsEventuallyMyParent(Widget *w);
};

// TODO: Get rid of these.
extern float debugDrawOffsetX;
extern float debugDrawOffsetY;

/// Load a widget from an XML element.
Widget *widgetCreateFromXMLElement(TiXmlElement *xml);

/// Load a widget from an XML document.
Widget *widgetCreateFromXMLFile(const std::string &fileName);

/// Go from one coordinate space to another to get where the mouse is
/// pointing in the transformed coordinate space on the specificed Z
/// plane.
Vector2D widgetProjectOntoZPlane(const Vector2D &pos, const Matrix4x4 &matrix, float zPlane = 0);

/// Go from GL viewport coordinates ( (-1, 1) to (1, -1) ) to 3D
/// coordinates where the line projected from that coordinate hits the
/// Z plane.
Vector2D widgetProjectOntoGLZPlane(const Vector2D &in, float zPlane = 0);

/// Go from window coordinates to GL coordinates. (But before going into
/// the projection and modelview matrices.
Vector2D widgetWindowToViewport(const Vector2D &windowSpaceCoords);

/// Set up Lua globals.
void widgetInitLua(lua_State *L);

/// This is the main "root" Widget. Everything is a child of this. It's
/// actually only used in three ways: It's something the main code can
/// access globally, and it's something that Lua code can set. Finally,
/// attempting to find a Widget by name will start here in its search.
extern Widget* widgetRoot;

/// Get a widget by its UID from the reference system.
Widget *widgetGetByUID(const std::string &uid);

/// Clean up any destroyed Widgets that were removed by the Lua code.
/// This is a safety net so that Lua stuff doesn't delete anything that
/// is also being used. For example deleting something while in its own
/// even handler.
void widgetCleanupDeletedWidgets(void);

/// Convert an SDL event to a WidgetEvent.
void widgetConvertSDLEvent(const SDL_Event *sdlEvent, WidgetEvent *widgetEvent);

void widgetPushTransforms(float screenWidth, float screenHeight);
void widgetPopTransforms(void);

/// Get the event at the top of the event stack.
const WidgetEvent *widgetGetEvent(void);

/// Set a default value for new Widgets to use. Names are up to the
/// widgets that use them.
void widgetSetDefaultStr(const std::string &name, const std::string &value);

/// Get a default value.
std::string widgetGetDefaultStr(const std::string &name);

/// Add a pointer to a unsigned int to be set to 0 any time
/// widgetInvalidateDisplayLists() is called.
void widgetAddDisplayListSentinel(unsigned int *l);

/// Remove a display list watcher.
void widgetRemoveDisplayListSentinel(unsigned int *l);

/// Invalidate all display lists. Use in case some Windows user wants a
/// window resize or something else unreasonably reasonable.
void widgetInvalidateDisplayLists(void);

/// Check to see if a Widget has already been moved onto the safe
/// deletion list.
bool widgetIsAlreadySafeDeleted(Widget *w);

/// Just add a Widget to the safe delete list so it can be cleaned up
/// later.
void widgetSafeDelete(Widget *w);

#endif

 
