/*
CGUI

--Overview--

    This is the top class of the whole GUI, all objects
    and settings are stored within this class.

--More info--

    Check GUI.h

*/

#ifndef INCLUDED_CGUI
#define INCLUDED_CGUI

#include "ps/Errors.h"
ERROR_GROUP(GUI);
ERROR_TYPE(GUI, JSOpenFailed);

//--------------------------------------------------------
//  Includes / Compiler directives
//--------------------------------------------------------
// NOTE: GUI.h included at the bottom of this file (has to be after CGUI class
// definition)

#include "GUITooltip.h"
#include "GUIbase.h"

#include "ps/Overlay.h" // CPos and CRect

#include "ps/Singleton.h"
#include "lib/input.h"

#include "ps/XML/Xeromyces.h"

extern InReaction gui_handler(const SDL_Event_* ev);

//--------------------------------------------------------
//  Macros
//--------------------------------------------------------

//--------------------------------------------------------
//  Types
//--------------------------------------------------------

//--------------------------------------------------------
//  Error declarations
//--------------------------------------------------------

//--------------------------------------------------------
//  Declarations
//--------------------------------------------------------

/**
 * Contains a list of values for new defaults to objects.
 */
struct SGUIStyle
{
    // A list of defaults for 
    std::map<CStr, CStr> m_SettingsDefaults;
};

struct JSObject; // The GUI stores a JSObject*, so needs to know that JSObject exists
class IGUIObject;
class CGUISpriteInstance;
struct SGUIText;
struct CColor;
struct SGUIText;
struct SGUIIcon;
class CGUIString;
class CGUISprite;
struct SGUIImageEffects;
struct SGUIScrollBarStyle;
class GUITooltip;

/**
 * The main object that includes the whole GUI. Is singleton
 * and accessed by g_GUI.
 *
 * No interfacial functions throws.
 */
class CGUI : public Singleton<CGUI>
{
    friend class IGUIObject;
    friend class IGUIScrollBarOwner;
    friend class CInternalCGUIAccessorBase;

private:
    // Private typedefs
    typedef IGUIObject *(*ConstructObjectFunction)();

public:
    CGUI();
    ~CGUI();

    /**
     * Initializes the GUI, needs to be called before the GUI is used
     */
    void Initialize();
    
    /**
     * @deprecated Will be removed
     */
    void Process();

    /**
     * Performs processing that should happen every frame (including the "Tick" event)
     */
    void TickObjects();

    /**
     * Sends a specified event to every object
     */
    void SendEventToAll(const CStr& EventName);

    /**
     * Displays the whole GUI
     */
    void Draw();

    /**
     * Draw GUI Sprite
     *
     * @param Sprite Object referring to the sprite (which also caches
     *        calculations for faster rendering)
     * @param CellID Number of the icon cell to use. (Ignored if this sprite doesn't
     *        have any images with "cell-size")
     * @param Z Drawing order, depth value
     * @param Rect Position and Size
     * @param Clipping The sprite shouldn't be drawn outside this rectangle
     */
    void DrawSprite(CGUISpriteInstance& Sprite, int CellID, const float &Z, 
                    const CRect &Rect, const CRect &Clipping=CRect());

    /**
     * Draw a SGUIText object
     *
     * @param Text Text object.
     * @param DefaultColor Color used if no tag applied.
     * @param pos position
     * @param z z value.
     */
    void DrawText(SGUIText &Text, const CColor &DefaultColor, 
                  const CPos &pos, const float &z, const CRect &clipping);

    /**
     * Clean up, call this to clean up all memory allocated
     * within the GUI.
     */
    void Destroy();

    /**
     * The replacement of Process(), handles an SDL_Event_
     *
     * @param ev SDL Event, like mouse/keyboard input
     */
    InReaction HandleEvent(const SDL_Event_* ev);

    /**
     * Load a GUI XML file into the GUI.
     *
     * <b>VERY IMPORTANT!</b> All \<styles\>-files must be read before
     * everything else!
     *
     * @param Filename Name of file
     */
    void LoadXmlFile(const std::string &Filename);

    /**
     * Checks if object exists and return true or false accordingly
     *
     * @param Name String name of object
     * @return true if object exists
     */
    bool ObjectExists(const CStr& Name) const;


    /**
     * Returns the GUI object with the desired name, or NULL
     * if no match is found,
     *
     * @param Name String name of object
     * @return Matching object, or NULL
     */
    IGUIObject* FindObjectByName(const CStr& Name) const;

    /**
     * The GUI needs to have all object types inputted and
     * their constructors. Also it needs to associate a type
     * by a string name of the type.
     * 
     * To add a type:
     * @code
     * AddObjectType("button", &CButton::ConstructObject);
     * @endcode
     *
     * @param str Reference name of object type
     * @param pFunc Pointer of function ConstuctObject() in the object
     *
     * @see CGUI#ConstructObject()
     */
    void AddObjectType(const CStr& str, ConstructObjectFunction pFunc) { m_ObjectTypes[str] = pFunc; }

    /**
     * Update Resolution, should be called every time the resolution
     * of the OpenGL screen has been changed, this is because it needs
     * to re-cache all its actual sizes
     *
     * Needs no input since screen resolution is global.
     *
     * @see IGUIObject#UpdateCachedSize()
     */
    void UpdateResolution();

    /**
     * Generate a SGUIText object from the inputted string.
     * The function will break down the string and its
     * tags to calculate exactly which rendering queries
     * will be sent to the Renderer.
     *
     * Done through the CGUI since it can communicate with 
     *
     * @param Text Text to generate SGUIText object from
     * @param Font Default font, notice both Default color and default font
     *        can be changed by tags.
     * @param Width Width, 0 if no word-wrapping.
     * @param BufferZone space between text and edge, and space between text and images.
     *
     * pObject is *only* if error parsing fails, and we need to be able to output
     * which object the error occured in to aid the user. The parameter is completely
     * optional.
     */
    SGUIText GenerateText(const CGUIString &Text, const CStr& Font, 
                          const float &Width, const float &BufferZone,
                          const IGUIObject *pObject=NULL);

    /**
     * Returns the JSObject* associated with the GUI
     *
     * @return The relevant JS object
     */
    JSObject* GetScriptObject() { return m_ScriptObject; }

    /**
     * Check if an icon exists
     */
    bool IconExists(const CStr& str) const { return (m_Icons.count(str) != 0); }

    /**
     * Get Icon (a copy, can never be changed)
     */
    SGUIIcon GetIcon(const CStr& str) const { return m_Icons.find(str)->second; }

    /**
     * Get pre-defined color (if it exists)
     * Returns false if it fails.
     */
    bool GetPreDefinedColor(const CStr& name, CColor &Output);

private:

    /**
     * Updates the object pointers, needs to be called each
     * time an object has been added or removed.
     *
     * This function is atomic, meaning if it throws anything, it will
     * have seen it through that nothing was ultimately changed.
     *
     * @throws PS_RESULT that is thrown from IGUIObject::AddToPointersMap().
     */
    void UpdateObjects();

    /**
     * Adds an object to the GUI's object database
     * Private, since you can only add objects through 
     * XML files. Why? Because it enables the GUI to
     * be much more encapsulated and safe.
     *
     * @throws  Rethrows PS_RESULT from IGUIObject::SetGUI() and
     *          IGUIObject::AddChild().
     */
    void AddObject(IGUIObject* pObject);

    /**
     * Report XML Reading Error, should be called from within the
     * Xerces_* functions.
     *
     * @param str Error message
     */
    void ReportParseError(const char *str, ...);

    /**
     * You input the name of the object type, and let's
     * say you input "button", then it will construct a
     * CGUIObjet* as a CButton.
     *
     * @param str Name of object type
     * @return Newly constructed IGUIObject (but constructed as a subclass)
     */
    IGUIObject *ConstructObject(const CStr& str);

    /**
     * Get Focused Object.
     */
    IGUIObject *GetFocusedObject() { return m_FocusedObject; }

    //--------------------------------------------------------
    /** @name XML Reading Xeromyces specific subroutines
     *
     * These does not throw!
     * Because when reading in XML files, it won't be fatal
     * if an error occurs, perhaps one particular object
     * fails, but it'll still continue reading in the next.
     * All Error are reported with ReportParseError
     */
    //--------------------------------------------------------

    /**
        Xeromyces_* functions tree
        <objects> (ReadRootObjects)
         |
         +-<script> (ReadScript)
         |
         +-<object> (ReadObject)
            |
            +-<action>
            |
            +-Optional Type Extensions (IGUIObject::ReadExtendedElement) TODO
            |
            +-«object» *recursive*


        <styles> (ReadRootStyles)
         |
         +-<style> (ReadStyle)


        <sprites> (ReadRootSprites)
         |
         +-<sprite> (ReadSprite)
            |
            +-<image> (ReadImage)


        <setup> (ReadRootSetup)
         |
         +-<tooltip> (ReadToolTip)
         |
         +-<scrollbar> (ReadScrollBar)
         |
         +-<icon> (ReadIcon)
         |
         +-<color> (ReadColor)
    */
    //@{

    // Read Roots

    /**
     * Reads in the root element \<objects\> (the DOMElement).
     *
     * @param Element   The Xeromyces object that represents
     *                  the objects-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the root element \<sprites\> (the DOMElement).
     *
     * @param Element   The Xeromyces object that represents
     *                  the sprites-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadRootSprites(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the root element <styles> (the DOMElement).
     *
     * @param Element   The Xeromyces object that represents
     *                  the styles-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadRootStyles(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the root element \<setup\> (the DOMElement).
     *
     * @param Element   The Xeromyces object that represents
     *                  the setup-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadRootSetup(XMBElement Element, CXeromyces* pFile);

    // Read Subs

    /**
     * Notice! Recursive function!
     *
     * Read in an <object> (the XMBElement) and stores it
     * as a child in the pParent.
     *
     * It will also check the object's children and call this function
     * on them too. Also it will call all other functions that reads
     * in other stuff that can be found within an object. Check the
     * tree in the beginning of this class' Xeromyces_* section.
     *
     * Reads in the root element <sprites> (the DOMElement).
     *
     * @param Element   The Xeromyces object that represents
     *                  the object-tag.
     * @param pFile     The Xeromyces object for the file being read
     * @param pParent   Parent to add this object as child in.
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject *pParent);

    /**
     * Reads in the element <script> (the XMBElement) and executes
     * the script's code.
     *
     * @param Element   The Xeromyces object that represents
     *                  the sprite-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadScript(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the element <sprite> (the XMBElement) and stores the
     * result in a new CGUISprite.
     *
     * @param Element   The Xeromyces object that represents
     *                  the sprite-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the element <image> (the XMBElement) and stores the
     * result within the CGUISprite.
     *
     * @param Element   The Xeromyces object that represents
     *                  the image-tag.
     * @param pFile     The Xeromyces object for the file being read
     * @param parent    Parent sprite.
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadImage(XMBElement Element, CXeromyces* pFile, CGUISprite &parent);

    // TODO: Documentation. (I'm feeling lazy at the moment.)
    void Xeromyces_ReadEffects(XMBElement Element, CXeromyces* pFile, SGUIImageEffects &effects);

    /**
     * Reads in the element <style> (the XMBElement) and stores the
     * result in m_Styles.
     *
     * @param Element   The Xeromyces object that represents
     *                  the style-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadStyle(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the element <scrollbar> (the XMBElement) and stores the
     * result in m_ScrollBarStyles.
     *
     * @param Element   The Xeromyces object that represents
     *                  the scrollbar-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadScrollBarStyle(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the element <icon> (the XMBElement) and stores the
     * result in m_Icons.
     *
     * @param Element   The Xeromyces object that represents
     *                  the scrollbar-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadIcon(XMBElement Element, CXeromyces* pFile);
    
    /**
     * Reads in the element <tooltip> (the XMBElement) and stores the
     * result as an object with the name __tooltip_#.
     *
     * @param Element   The Xeromyces object that represents
     *                  the scrollbar-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadTooltip(XMBElement Element, CXeromyces* pFile);

    /**
     * Reads in the element <color> (the XMBElement) and stores the
     * result in m_PreDefinedColors
     *
     * @param Element   The Xeromyces object that represents
     *                  the scrollbar-tag.
     * @param pFile     The Xeromyces object for the file being read
     *
     * @see LoadXmlFile()
     */
    void Xeromyces_ReadColor(XMBElement Element, CXeromyces* pFile);

    //@}

private:

    // Variables

    //--------------------------------------------------------
    /** @name Miscellaneous */
    //--------------------------------------------------------
    //@{

    /**
     * An JSObject* under which all GUI JavaScript things will
     * be created, so that they can be garbage-collected
     * when the GUI shuts down.
     */
    JSObject* m_ScriptObject;

    /**
     * don't want to pass this around with the 
     * ChooseMouseOverAndClosest broadcast -
     * we'd need to pack this and pNearest in a struct
     */
    CPos m_MousePos;

    /**
     * Indicates which buttons are pressed (bit 0 = LMB,
     * bit 1 = RMB, bit 2 = MMB)
     */
    unsigned int m_MouseButtons;

    /// Used when reading in XML files
    // TODO Gee: Used?
    int16_t m_Errors;

    // Tooltip
    GUITooltip m_Tooltip;

    /**
     * This is a bank of custom colors, it is simply a look up table that
     * will return a color object when someone inputs the name of that
     * color. Of course the colors have to be declared in XML, there are
     * no hard-coded values.
     */
    std::map<CStr, CColor>  m_PreDefinedColors;

    //@}
    //--------------------------------------------------------
    /** @name Objects */
    //--------------------------------------------------------
    //@{

    /**
     * Base Object, all its children are considered parentless
     * because this is not a real object per se.
     */
    IGUIObject* m_BaseObject;

    /**
     * Focused object!
     * Say an input box that is selected. That one is focused.
     * There can only be one focused object.
     */
    IGUIObject* m_FocusedObject;

    /** 
     * Just pointers for fast name access, each object
     * is really constructed within its parent for easy
     * recursive management.
     * Notice m_BaseObject won't belong here since it's
     * not considered a real object.
     */
    map_pObjects m_pAllObjects;

    /**
     * Number of object that has been given name automatically.
     * the name given will be '__internal(#)', the number (#)
     * being this variable. When an object's name has been set
     * as followed, the value will increment.
     */
    int m_InternalNameNumber;

    /**
     * Function pointers to functions that constructs
     * IGUIObjects by name... For instance m_ObjectTypes["button"]
     * is filled with a function that will "return new CButton();"
     */
    std::map<CStr, ConstructObjectFunction> m_ObjectTypes;

    //--------------------------------------------------------
    //  Databases
    //--------------------------------------------------------

    // Sprites
    std::map<CStr, CGUISprite> m_Sprites;

    // Styles
    std::map<CStr, SGUIStyle> m_Styles;

    // Scroll-bar styles
    std::map<CStr, SGUIScrollBarStyle> m_ScrollBarStyles;

    // Icons
    std::map<CStr, SGUIIcon> m_Icons;
};


#include "GUI.h"

#endif