#pragma once

#include <SFGUI/Config.hpp>

#include <map>
#include <memory>
#include <functional>

namespace sfg {

/** Widget signal.
 * Calls a function if something interesting is happening in a widget. Signals
 * can be connected to multiple endpoints. An endpoint may be anything that fits
 * into a std::function<void()>.
 *
 * For free functions, just pass the function's pointer to Connect(). For
 * member functions (methods) use a lambda function.
 * Examples:\n
 * \code
 * widget->OnLeftClick.Connect( &my_callback ); // Free function binding.
 * widget->OnLeftClick.Connect( [object] { object->MyCallback(); } ); // Method binding via lambda function.
 * \endcode
 *
 */
class SFGUI_API Signal {
	public:
		typedef std::size_t SignalID; //!< Signal ID type.

		Signal() = default;
		Signal( const Signal& ) = delete;
		Signal& operator=( const Signal& ) = delete;

		Signal( Signal&& other );
		Signal& operator=( Signal&& other );

		/** Connect to free function.
		 * @param delegate Free function.
		 * @return Connection serial, use for disconnecting.
		 */
		unsigned int Connect( std::function<void()> delegate );

		/** Disconnect signal.
		 * @param serial Serial generated by Connect().
		 */
		void Disconnect( unsigned int serial );

		/** Emit.
		 */
		void operator()() const;

		/** Generate a GUID for a signal.
		 * @return Signal GUID.
		 */
		static SignalID GetGUID();

	private:
		typedef std::map<unsigned int, std::function<void()>> DelegateMap;

		std::unique_ptr<DelegateMap> m_delegates;
};

/** Widget signal container
 * Should only be used internally to store signals.
 * Needed to reduce memory consumption of widgets without any connected handlers.
 */
class SFGUI_API SignalContainer {
	public:
		/** Access signal.
		 * @param id Signal ID.
		 * @return Reference to the requested signal.
		 */
		Signal& operator[]( const Signal::SignalID& id );

		/** Emit signal.
		 * @param id Signal ID.
		 */
		void Emit( const Signal::SignalID& id );

	private:
		typedef std::map<Signal::SignalID, Signal> SignalMap;

		std::unique_ptr<SignalMap> m_signals;
};

}
