// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#pragma once

#include "IGUIEnvironment.h"
#include "IGUIElement.h"
#include "irrArray.h"
#include "IFileSystem.h"
#include "IOSOperator.h"

namespace irr
{

namespace gui
{

class CGUIEnvironment : public IGUIEnvironment, public IGUIElement
{
public:

	//! constructor
	CGUIEnvironment(io::IFileSystem* fs, video::IVideoDriver* driver, IOSOperator* op);

	//! destructor
	virtual ~CGUIEnvironment();

	//! draws all gui elements
	void drawAll(bool useScreenSize) override;

	//! returns the current video driver
	video::IVideoDriver* getVideoDriver() const override;

	//! returns pointer to the filesystem
	io::IFileSystem* getFileSystem() const override;

	//! returns a pointer to the OS operator
	IOSOperator* getOSOperator() const override;

	//! posts an input event to the environment
	bool postEventFromUser(const SEvent& event) override;

	//! This sets a new event receiver for gui events. Usually you do not have to
	//! use this method, it is used by the internal engine.
	void setUserEventReceiver(IEventReceiver* evr) override;

	//! removes all elements from the environment
	void clear() override;

	//! called if an event happened.
	bool OnEvent(const SEvent& event) override;

	//! returns the current gui skin
	IGUISkin* getSkin() const override;

	//! Sets a new GUI Skin
	void setSkin(IGUISkin* skin) override;

	//! Creates a new GUI Skin based on a template.
	/** \return Returns a pointer to the created skin.
	If you no longer need the skin, you should call IGUISkin::drop().
	See IReferenceCounted::drop() for more information. */
	IGUISkin* createSkin(EGUI_SKIN_TYPE type) override;

	//! Creates the image list from the given texture.
	virtual IGUIImageList* createImageList(video::ITexture* texture,
					core::dimension2d<s32> imageSize, bool useAlphaChannel) override;

	//! returns the font
	IGUIFont* getFont(const io::path& filename) override;

	//! add an externally loaded font
	IGUIFont* addFont(const io::path& name, IGUIFont* font) override;

	//! remove loaded font
	void removeFont(IGUIFont* font) override;

	//! returns default font
	IGUIFont* getBuiltInFont() const override;

	//! returns the sprite bank
	IGUISpriteBank* getSpriteBank(const io::path& filename) override;

	//! returns the sprite bank
	IGUISpriteBank* addEmptySpriteBank(const io::path& name) override;

	//! adds an button. The returned pointer must not be dropped.
	IGUIButton* addButton(const core::rect<s32>& rectangle, IGUIElement* parent=0, s32 id=-1, const wchar_t* text=0,const wchar_t* tooltiptext = 0) override;

	//! adds a scrollbar. The returned pointer must not be dropped.
	virtual IGUIScrollBar* addScrollBar(bool horizontal, const core::rect<s32>& rectangle,
		IGUIElement* parent=0, s32 id=-1) override;

	//! Adds an image element.
	virtual IGUIImage* addImage(video::ITexture* image, core::position2d<s32> pos,
		bool useAlphaChannel=true, IGUIElement* parent=0, s32 id=-1, const wchar_t* text=0) override;

	//! adds an image. The returned pointer must not be dropped.
	virtual IGUIImage* addImage(const core::rect<s32>& rectangle,
		IGUIElement* parent=0, s32 id=-1, const wchar_t* text=0, bool useAlphaChannel=true) override;

	//! adds a checkbox
	virtual IGUICheckBox* addCheckBox(bool checked, const core::rect<s32>& rectangle,
		IGUIElement* parent=0, s32 id=-1, const wchar_t* text=0) override;

	//! adds a list box
	virtual IGUIListBox* addListBox(const core::rect<s32>& rectangle,
		IGUIElement* parent=0, s32 id=-1, bool drawBackground=false) override;

	//! Adds a file open dialog.
	virtual IGUIFileOpenDialog* addFileOpenDialog(const wchar_t* title = 0,
			bool modal=true, IGUIElement* parent=0, s32 id=-1,
			bool restoreCWD=false, io::path::char_type* startDir=0) override;

	//! adds a static text. The returned pointer must not be dropped.
	virtual IGUIStaticText* addStaticText(const wchar_t* text, const core::rect<s32>& rectangle,
		bool border=false, bool wordWrap=true, IGUIElement* parent=0, s32 id=-1, bool drawBackground = false) override;

	//! Adds an edit box. The returned pointer must not be dropped.
	virtual IGUIEditBox* addEditBox(const wchar_t* text, const core::rect<s32>& rectangle,
		bool border=false, IGUIElement* parent=0, s32 id=-1) override;

	//! Adds a tab control to the environment.
	virtual IGUITabControl* addTabControl(const core::rect<s32>& rectangle,
		IGUIElement* parent=0, bool fillbackground=false, bool border=true, s32 id=-1) override;

	//! Adds tab to the environment.
	virtual IGUITab* addTab(const core::rect<s32>& rectangle,
		IGUIElement* parent=0, s32 id=-1) override;

	//! Adds a combo box to the environment.
	virtual IGUIComboBox* addComboBox(const core::rect<s32>& rectangle,
		IGUIElement* parent=0, s32 id=-1) override;

	//! sets the focus to an element
	bool setFocus(IGUIElement* element) override;

	//! removes the focus from an element
	bool removeFocus(IGUIElement* element) override;

	//! Returns if the element has focus
	bool hasFocus(const IGUIElement* element, bool checkSubElements=false) const override;

	//! Returns the element with the focus
	IGUIElement* getFocus() const override;

	//! Returns the element last known to be under the mouse
	IGUIElement* getHovered() const override;

	//! Returns the root gui element.
	IGUIElement* getRootGUIElement() override;

	void OnPostRender( u32 time ) override;

	//! Find the next element which would be selected when pressing the tab-key
	IGUIElement* getNextElement(bool reverse=false, bool group=false) override;

	//! Set the way the gui will handle focus changes
	void setFocusBehavior(u32 flags) override;

	//! Get the way the gui does handle focus changes
	u32 getFocusBehavior() const override;

	//! Adds a IGUIElement to deletion queue.
	void addToDeletionQueue(IGUIElement* element) override;

private:

	//! clears the deletion queue
	void clearDeletionQueue();

	void updateHoveredElement(core::position2d<s32> mousePos);

	void loadBuiltInFont();

	struct SFont
	{
		io::SNamedPath NamedPath;
		IGUIFont* Font;

		bool operator < (const SFont& other) const
		{
			return (NamedPath < other.NamedPath);
		}
	};

	struct SSpriteBank
	{
		io::SNamedPath NamedPath;
		IGUISpriteBank* Bank;

		bool operator < (const SSpriteBank& other) const
		{
			return (NamedPath < other.NamedPath);
		}
	};

	struct SToolTip
	{
		IGUIStaticText* Element;
		u32 LastTime;
		u32 EnterTime;
		u32 LaunchTime;
		u32 RelaunchTime;
	};

	SToolTip ToolTip;

	core::array<SFont> Fonts;
	core::array<SSpriteBank> Banks;
	video::IVideoDriver* Driver;
	IGUIElement* Hovered;
	IGUIElement* HoveredNoSubelement;	// subelements replaced by their parent, so you only have 'real' elements here
	IGUIElement* Focus;
	core::position2d<s32> LastHoveredMousePos;
	IGUISkin* CurrentSkin;
	io::IFileSystem* FileSystem;
	IEventReceiver* UserReceiver;
	IOSOperator* Operator;
	u32 FocusFlags;
	core::array<IGUIElement*> DeletionQueue;

	static const io::path DefaultFontName;
};

} // end namespace gui
} // end namespace irr
