/*
 *  psychlops_widgets_event.h
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2011/02/05 by Kenchi HOSOKAWA
 *  (C) 2011 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */

#ifndef HEADER_PSYCHLOPS_WIDGET_EVENT
#define HEADER_PSYCHLOPS_WIDGET_EVENT


#include <typeinfo>
#include <vector>
#include <list>
#include <map>
//#include "../../../psychlops_core.h"
#include "../../../core/graphic/psychlops_g_shape.h"

namespace Psychlops
{
	class Canvas;


	class Event
	{
		public:
		virtual const char* getName() const = 0;
	};

namespace Events
{
	class Emittable;
	class Container;
	class RootContainer;


	class PointerDeviceEvent : public Event
	{
		public:
		long time;
		Point place;

		long getTime();
		Point getPlace();

		PointerDeviceEvent();
		static const char* name; virtual const char* getName() const;
	};

	class MouseLeftDown : public PointerDeviceEvent { public:
		static const char* name; virtual const char* getName() const;
		MouseLeftDown();
		MouseLeftDown(long when, Point where);
	};
	class MouseLeftUp : public PointerDeviceEvent { public:
		static const char* name; virtual const char* getName() const;
		MouseLeftUp();
		MouseLeftUp(long when, Point where);
	};
	class MouseRightDown : public PointerDeviceEvent { public:
		static const char* name; virtual const char* getName() const;
		MouseRightDown();
		MouseRightDown(long when, Point where);
	};
	class MouseRightUp : public PointerDeviceEvent { public:
		static const char* name; virtual const char* getName() const;
		MouseRightUp();
		MouseRightUp(long when, Point where);
	};


	class KeyEvent : public Event
	{
		public:
		long time;
		unsigned int code;

		KeyEvent();
		static const char* name; virtual const char* getName() const;
	};
	class KeyDown : public KeyEvent { public:
		static const char* name; virtual const char* getName() const;
		KeyDown();
		KeyDown(long when, unsigned int where);
	};
	class KeyUp : public KeyEvent { public:
		static const char* name; virtual const char* getName() const;
		KeyUp();
		KeyUp(long when, unsigned int where);
	};

	class Focus : public Event { public:
		static const char* name; virtual const char* getName() const;
		Focus();
	};

	class Blur : public Event { public:
		static const char* name; virtual const char* getName() const;
		Blur();
	};
	class Evoke : public Event { public:
		static const char* name; virtual const char* getName() const;
		Evoke();
	};

	class AddToTabstopList : public Event { public:
		static const char* name; virtual const char* getName() const;

		Emittable* target;
		AddToTabstopList(Emittable *e);
	};




	class Callable__ { public: virtual void call(Event &event) = 0; };
	template <typename T> class ActionDelegate__ : public Callable__
	{
		public: typedef void (T::*Fn)();
		private: T* obj; Fn fn;
		public:
		ActionDelegate__(T* o, Fn f) : obj(o), fn(f) { }
		virtual void call(Event &event) { (obj->*fn)(); }
	};
	template <typename T> Callable__* createActionDelegate(T* obj, void (T::*fn)()) { return new ActionDelegate__<T>(obj, fn); }

	class ActionFunction__ : public Callable__
	{
		public: typedef void (*Fn)();
		private: Fn fn;
		public:
		ActionFunction__(Fn f);
		virtual void call(Event &event);
	};
	Callable__* createActionFunction(void (*fn)());

	template <typename T, typename U> class EventDelegate__ : public Callable__
	{
		public: typedef void (T::*Fn)(U &);
		private: T* obj; Fn fn;
		public:
		EventDelegate__(T* o, Fn f) : obj(o), fn(f) { }
		virtual void call(Event &event) { U* u = (U*)(&event); (obj->*fn)(*u); }
	};
	template <typename T, typename U> Callable__* createEventDelegate(T* obj, void (T::*fn)(U &)) { return new EventDelegate__<T,U>(obj, fn); }

	template <typename U> class EventFunction__ : public Callable__
	{
		public: typedef void (*Fn)(U &);
		private: Fn fn;
		public:
		EventFunction__(Fn f) : fn(f) { }
		virtual void call(Event &event) { U* u = (U*)(&event); (*fn)(*u); }
	};
	template <typename U> Callable__* createEventFunction(void (*fn)(U &)) { return new EventFunction__<U>(fn); }


	typedef Callable__* DG;
	//typedef void (*DG)(Event&);
	typedef std::map<const char*, std::list<DG> > Slots;
	class Dispatcher {
		private:
		Slots slot;

		public:
		~Dispatcher();
		template<class U, class T> void connectActionDelegate(T* obj, void (T::*cb)()) {
			U u;
			const char* info = u.getName();
			if(slot.count(info) == 0) {
				slot[info] = std::list<DG>();
			}
			slot[info].push_back(createActionDelegate(obj, cb));
		}
		template<class U> void connectActionFunction(void (*cb)()) {
			U u;
			const char* info = u.getName();
			if(slot.count(info) == 0) {
				slot[info] = std::list<DG>();
			}
			slot[info].push_back(createActionFunction(cb));
		}
		template<class T, class U> void connectEventDelegate(T* obj, void (T::*cb)(U&)) {
			U u;
			const char* info = u.getName();
			if(slot.count(info) == 0) {
				slot[info] = std::list<DG>();
			}
			slot[info].push_back(createEventDelegate(obj, cb));
		}
		template<class U> void connectEventFunction(void (*cb)(U&)) {
			U u;
			const char* info = u.getName();
			if(slot.count(info) == 0) {
				slot[info] = std::list<DG>();
			}
			slot[info].push_back(createEventFunction(cb));
		}

		template<class T> void disconnect(void (*cb)(T&)) {
			T t;
			//const char* info = t.getName();
			//if(slot.count(info) == 1)
			//	slot[info].remove(cb);
		}

		void emit(Event &event);
		//void emit(Events::PointerDeviceEvent &event);
	};





	class Emittable
	{
		public:
		virtual Point getHitDatum() = 0;
		virtual Rectangle getArea() = 0;
		virtual Container& getParent() = 0;
		virtual Container& setParent(Container&) = 0;
		virtual Dispatcher& getSlots() = 0;
		virtual void distribute(Event &ev) = 0;
		virtual void distribute(Events::PointerDeviceEvent &ev) = 0;

		void bubble(Event &ev);
	};

	class EmittableBase : public Emittable
	{
		protected:
		Rectangle area_;
		Container *parent_;
		Dispatcher slots_;

		public:
		EmittableBase();
		virtual Point getHitDatum();
		virtual Rectangle getArea();
		virtual Container& getParent();
		virtual Container& setParent(Container &par);
		virtual Dispatcher& getSlots();
		virtual void distribute(Event &ev);
		virtual void distribute(Events::PointerDeviceEvent &event);
	};

	class Container : public Emittable
	{
		public:
		virtual void append(Emittable &target) = 0;
		virtual void remove(Emittable &target) = 0;
		virtual void getBubble(Event &ev) = 0;
	};


	class ContainerBase : public Container
	{
		protected:
		Rectangle area_;
		Container *parent_;
		Dispatcher slots_;
		std::vector<Emittable *> children_;

		public:
		ContainerBase();
		virtual void append(Emittable &target);
		virtual void remove(Emittable &target);
		virtual void getBubble(Event &ev);


		virtual Point getHitDatum();
		virtual Rectangle getArea();
		virtual Container& getParent();
		virtual Container& setParent(Container &par);
		virtual Dispatcher& getSlots();
		virtual void distribute(Event &ev);
		virtual void distribute(Events::PointerDeviceEvent &event);
	};


	class RootContainer : public ContainerBase
	{
		friend class ::Psychlops::Canvas;
		public:
		Canvas *root;
		protected:
		std::vector<Emittable *> tabstop_list;
		std::vector<void *> tabstop_list__;
		int focused, focused__;

		public:
		RootContainer();
		virtual void distributeToFocus(Event &ev);
		virtual void getBubble(Event &ev);

		void appendTabStop__(void *target);
		bool isFocused__(void *target);
		void nextTabStop();
		void prevTabStop();
	};



}
}

#endif
