#pragma once


#include <Resource/Folder.h>
#include <Resource/Image.h>
#include <Resource/DataArray.h>
#include <Engine/Layer.h>
#include <Utils/Any.h>
#include <Utils/Size2D.h>
#include <Utils/Point2D.h>
#include <Utils/OrderedCollection.h>
#include <Utils/Property.h>
#include <Widgets/Button.h>

#include <string>
#include <Widgets/Panel/PanelBase.h>
#include <Utils/Collection.h>
#include <Engine/Graphic2D.h>

using std::string;

namespace gaming { namespace menu {

	void Initialize();

	class Menu;

	class IOrganizer {
	public:

		virtual void Setup(Menu&)	= 0;

		virtual void Refresh()		= 0;

		virtual void Load(gge::resource::DataArray&)=0;

	};

	class IHider {
	public:

		virtual void Setup(Menu&)	= 0;

		virtual void Hide(bool)		= 0;

		virtual void Show(bool)		= 0;

		virtual void Load(gge::resource::DataArray&)=0;
	};

	class IMinimizer {
	public:

		virtual void Setup(Menu&)		= 0;

		virtual void Minimize(bool)		= 0;

		virtual void Restore (bool)		= 0;

		virtual void Refresh ()	{ }

		virtual void Load(gge::resource::DataArray&)=0;
	};


	//do not try to create menu interfaces manually.
	//only pass folder resources to create them
	//may be one day a method to create menus 
	//programmatically will code itself
	class Menu {

	public:

		class Factory {
		public:
			
			typedef IOrganizer *(*OrganizerCreatorFunction)();
			typedef IHider     *(*HiderCreatorFunction    )();
			typedef IMinimizer *(*MinimizerCreatorFunction)();

			class OrganizerCreator {
				friend Factory;
			public:
				OrganizerCreator(std::string ID, OrganizerCreatorFunction fn) : ID(ID), fn(fn)
				{ }

			protected:
				std::string ID;
				OrganizerCreatorFunction fn;
			};

			class HiderCreator {
				friend Factory;
			public:
				HiderCreator(std::string ID, HiderCreatorFunction fn) : ID(ID), fn(fn)
				{ }

			protected:
				std::string ID;
				HiderCreatorFunction fn;
			};

			class MinimizerCreator {
				friend Factory;
			public:
				MinimizerCreator(std::string ID, MinimizerCreatorFunction fn) : ID(ID), fn(fn)
				{ }

			protected:
				std::string ID;
				MinimizerCreatorFunction fn;
			};

			static IOrganizer *CreateOrganizer(Menu &Parent, gge::resource::DataArray &data);

			static IHider	  *CreateHider    (Menu &Parent, gge::resource::DataArray &data);

			static IMinimizer *CreateMinimizer(Menu &Parent, gge::resource::DataArray &data);

			static gge::utils::Collection<OrganizerCreator> Organizers;
			static gge::utils::Collection<HiderCreator>     Hiders;
			static gge::utils::Collection<MinimizerCreator> Minimizers;
		};


		class Button {
			friend class Menu;
		public:
			gge::utils::EventChain<Button> ClickEvent;

			gge::utils::TextualProperty<Button> Text;
			gge::utils::BooleanProperty<Button> Visible;
			gge::utils::BooleanProperty<Button> Disabled;
			//This is extra spacing provided to organize buttons into groups,
			//General space that would be set is 1/4 of the button per spacing.
			//This space is placed before the button
			gge::utils::NumericProperty<Button, int> Spacing;
			gge::utils::ReferenceProperty<Button, gge::graphics::RectangularGraphic2D> Icon;


		protected:
			Button(Menu& menu, std::string text="") : parent(menu), ClickEvent("click", this), 
			INIT_PROPERTY(Button, Text), text (text), spacing(0),
			INIT_PROPERTY(Button, Visible), visible (true),
			INIT_PROPERTY(Button, Disabled), disabled(false), icon(NULL),
			INIT_PROPERTY(Button, Icon),
			INIT_PROPERTY(Button, Spacing)
			{ }

			std::string text;
			void   setText(const std::string &value) { 
				if(text!=value) {
					text=value; 
					parent.Refresh();
				}
			}
			std::string getText() const { 
				return text; 
			}

			bool visible;
			void setVisible(const bool &value) { 
				if(visible!=value) {
					visible=value; 
					parent.Refresh(); 
				}
			}
			bool getVisible() const { 
				return visible; 
			}

			bool disabled;
			void setDisabled(const bool &value) {
				if(disabled!=value) {
					disabled=value; 
					parent.Refresh(); 
				}
			}
			bool getDisabled() const { 
				return disabled; 
			}

			int spacing;
			void setSpacing(const int &value) {
				if(spacing!=value) {
					spacing = value;
					parent.Refresh();
				}
			}
			int getSpacing() const {
				return spacing;
			}

			gge::graphics::RectangularGraphic2D *icon;
			void setIcon(gge::graphics::RectangularGraphic2D* value) {
				if(icon!=value) {
					icon=value;
					parent.Refresh();
				}
			}
			gge::graphics::RectangularGraphic2D *getIcon() const {
				return icon;
			}

			Menu   &parent;
		};
		



		Menu();

		void ObtainFrom(gge::resource::DataArray &data);

		void UseLayer(gge::LayerBase &layer);

		void Refresh();

		void Minimize(bool animate);

		void Restore(bool animate);

		void Hide(bool animate);
		
		void Show(bool animate);

		void Minimize() { Minimize(true); }

		void Restore() { Restore(true); }

		void Hide() { Hide(true); }
		
		void Show() { Show(true); }

		Button &AddButton(std::string Caption);

		Button &AddButton(std::string Caption, void(*Handler)()) {
			Menu::Button *btn=new Menu::Button(*this, Caption);
			btn->ClickEvent.Register(Handler);
			Buttons.Add(btn);

			Refresh();

			return *btn;
		}

		Button &AddButton(std::string Caption, std::function<void()> Handler);

		bool IsMinimized() { return minimized; }

		bool IsVisible() { return visible; }

		gge::LayerBase &MainLayer() { return main; }
		gge::LayerBase &MinimizeLayer() { return minimize; }



		gge::utils::OrderedCollection<Button> Buttons;
		Button RestoreButton;

		gge::utils::EventChain<Menu> HideComplete;
		gge::utils::EventChain<Menu> ShowComplete;
		gge::utils::EventChain<Menu> MinimizeComplete;
		gge::utils::EventChain<Menu> RestoreComplete;
		gge::utils::EventChain<Menu> RestoreStarted;
	

	protected:

		class C_Interfaces {
		public:			
			IOrganizer *Organizer;
			IHider     *Hider;
			IMinimizer *Minimizer;

			C_Interfaces() : Organizer(NULL), Hider(NULL), Minimizer(NULL)
			{ }

		} Interfaces;


		bool visible, minimized;

		gge::LayerBase layer;
		gge::LayerBase main;
		gge::LayerBase minimize;
	};

}}
