///////////////////////////////////////////////////////////////////////////////
// ketchup/message_processor.hpp
//
//  Copyright 2005 MB@unknown.jp.
//
// This file is a part of the *ketchup*.
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license. You must not remove this notice, or
// any other, from this software.
//
//  Revision History
//   05 May 2005 Initial version

#ifndef KETCHUP_MESSAGE_PROCESSOR_H
#define KETCHUP_MESSAGE_PROCESSOR_H

#ifdef _MSC_VER
#pragma once
#endif

#include <boost/concept_check.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/is_sequence.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/reverse_fold.hpp>

///////////////////////////////////////////////////////////////////////////////
// function parameters and arguments
//
#define KETCHUP_PROCESS_WINDOW_MESSAGE_PARAMS \
	HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID

#define KETCHUP_PROCESS_WINDOW_MESSAGE_ARGS \
	hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID

///////////////////////////////////////////////////////////////////////////////
// function body variables (bHandled added)
//
#define KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS \
	HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID, BOOL& bHandled

#define KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_ARGS \
	hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID, bHandled 

#define KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING() \
	boost::ignore_unused_variable_warning(hWnd);\
	boost::ignore_unused_variable_warning(uMsg);\
	boost::ignore_unused_variable_warning(wParam);\
	boost::ignore_unused_variable_warning(lParam);\
	boost::ignore_unused_variable_warning(lResult);\
	boost::ignore_unused_variable_warning(dwMsgMapID);\
	boost::ignore_unused_variable_warning(bHandled)

///////////////////////////////////////////////////////////////////////////////
// KETCHUP_CHAIN_MSG_MAP for ATL's BEGIN_MSG_MAP macro
// 
#define KETCHUP_CHAIN_MSG_MAP(MessageMap) {\
	if (process_window_message<MessageMap>(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID))\
		return TRUE;\
}

namespace ketchup { namespace detail {

namespace mpl = boost::mpl;
using mpl::_;

///////////////////////////////////////////////////////////////////////////////
// big_if_statement
//
template< class Derived, class MessageMap >
struct big_if_statement
{
	template< class State, class MessageMapEntry >
	struct op
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			if (MessageMapEntry::compile(derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_ARGS))
				return true;
			else
				return State::compile(derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_ARGS);
		}
	};

	struct init_state
	{
		static bool compile(Derived&, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();
			return false;
		}
	};

	typedef typename mpl::reverse_fold< MessageMap, init_state, op<_,_> >::type type;
};

///////////////////////////////////////////////////////////////////////////////
// process_window_message_function_body
//
template< class Derived, class MessageMap >
struct process_window_message_function_body
{
	static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_PARAMS)
	{
		BOOL bHandled = TRUE;
		typedef typename big_if_statement< Derived, MessageMap >::type statement;
		return statement::compile(derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_ARGS);
	}

	typedef process_window_message_function_body type;
};

///////////////////////////////////////////////////////////////////////////////
// process_window_message_function_body_for_cracked_handlers
//
template< class Derived, class MessageMap >
struct process_window_message_function_body_for_cracked_handlers
{
	static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_PARAMS)
	{
		bool old = derived.is_msg_handled();	// for recursive calls
		BOOL bHandled = TRUE;
		typedef typename big_if_statement< Derived, MessageMap >::type statement;
		bool ret = statement::compile(derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_ARGS);
		derived.set_msg_handled(old);
		return ret;
	}

	typedef process_window_message_function_body_for_cracked_handlers type;
};

///////////////////////////////////////////////////////////////////////////////
// message_processor_base
//
template< class Derived, class HasCrackedHandlers >
struct message_processor_base
{
	struct base
	{
		template< class MessageMap >
		bool process_window_message(KETCHUP_PROCESS_WINDOW_MESSAGE_PARAMS)
		{
			typedef typename process_window_message_function_body<Derived, MessageMap>::type body;
			return body::compile(*static_cast<Derived*>(this), KETCHUP_PROCESS_WINDOW_MESSAGE_ARGS);
		};

		typedef base type;
	};	

	struct base_for_cracked_handlers
	{
		bool msg_handled_;

		bool is_msg_handled() { return msg_handled_; }
		void set_msg_handled(bool handled) { msg_handled_ = handled; }

		template< class MessageMap >
		bool process_window_message(KETCHUP_PROCESS_WINDOW_MESSAGE_PARAMS)
		{
			typedef typename process_window_message_function_body_for_cracked_handlers<Derived, MessageMap>::type body;
			return body::compile(*static_cast<Derived*>(this), KETCHUP_PROCESS_WINDOW_MESSAGE_ARGS);
		};

		typedef base_for_cracked_handlers type;
	};

	typedef typename mpl::eval_if< HasCrackedHandlers,
		base_for_cracked_handlers,
		base
	>::type type;
};


} } // namespace ketchup::detail


namespace ketchup {

///////////////////////////////////////////////////////////////////////////////
// message_processor
//
template< class Derived, bool has_cracked_handlers = false >
struct message_processor :
	detail::message_processor_base< Derived, boost::mpl::bool_<has_cracked_handlers> >::type
{
protected:
	///////////////////////////////////////////////////////////////////////////////
	// MessageMapEntries
	//
	// Note: Default parameters in nested templates will be ignored VC7.1
	//
	
	///////////////////////////////////////////////////////////////////////////////
	// message_handler
	//
	typedef LRESULT (Derived::*message_handler_type)(UINT, WPARAM, LPARAM, BOOL&);

	template< UINT msg, message_handler_type func >
	struct message_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == msg) {
				bHandled = TRUE;
				lResult = (derived.*func)(uMsg, wParam, lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	template< UINT msg, message_handler_type func >
	struct message_handler_hot
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == msg) {
				lResult = (derived.*func)(uMsg, wParam, lParam, bHandled);
				return true;
			}
			return false;
		}
	};

	template< UINT msg, message_handler_type func >
	struct message_handler_not
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == msg) {
				lResult = (derived.*func)(uMsg, wParam, lParam, bHandled);
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// message_range_handler
	//
	template< UINT msgFirst, UINT msgLast, message_handler_type func >
	struct message_range_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg >= msgFirst && uMsg <= msgLast) {
				bHandled = TRUE;
				lResult = (derived.*func)(uMsg, wParam, lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// command_handler
	//
	typedef LRESULT (Derived::*command_handler_type)(WORD, WORD, HWND, BOOL&);

	template< WORD id, WORD code, command_handler_type func >
	struct command_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_COMMAND && id == LOWORD(wParam) && code == HIWORD(wParam)) {
				bHandled = TRUE;
				lResult = (derived.*func)(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// command_id_handler
	//
	template< WORD id, command_handler_type func >
	struct command_id_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if(uMsg == WM_COMMAND && id == LOWORD(wParam)) {
				bHandled = TRUE;
				lResult = (derived.*func)(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	template< WORD id, command_handler_type func >
	struct command_id_handler_hot
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if(uMsg == WM_COMMAND && id == LOWORD(wParam)) {
				lResult = (derived.*func)(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);
				return true;
			}
			return false;
		}
	};

	template< WORD id, command_handler_type func >
	struct command_id_handler_not
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if(uMsg == WM_COMMAND && id == LOWORD(wParam)) {
				lResult = (derived.*func)(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// command_code_handler
	//
	template< WORD code, command_handler_type func >
	struct command_code_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if(uMsg == WM_COMMAND && id == HIWORD(wParam)) {
				bHandled = TRUE;
				lResult = (derived.*func)(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// command_range_handler
	//
	template< WORD idFirst, WORD idLast, command_handler_type func >
	struct command_range_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if(uMsg == WM_COMMAND && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) {
				bHandled = TRUE;
				lResult = (derived.*func)(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// command_range_code_handler
	//
	template< WORD idFirst, WORD idLast, WORD code, command_handler_type func >
	struct command_range_code_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if(uMsg == WM_COMMAND && code == HIWORD(wParam) && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) {
				bHandled = TRUE;
				lResult = (derived.*func)(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// notify_handler
	//
	typedef LRESULT (Derived::*notify_handler_type)(int, LPNMHDR, BOOL&);

	template< UINT id, UINT cd, notify_handler_type func >
	struct notify_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom && cd == ((LPNMHDR)lParam)->code) {
				bHandled = TRUE;
				lResult = (derived.*func)((int)wParam, (LPNMHDR)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// notify_id_handler
	//
	template< UINT id, notify_handler_type func >
	struct notify_id_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) {
				bHandled = TRUE;
				lResult = (derived.*func)((int)wParam, (LPNMHDR)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// notify_code_handler
	//
	template< UINT cd, notify_handler_type func >
	struct notify_code_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) {
				bHandled = TRUE;
				lResult = (derived.*func)((int)wParam, (LPNMHDR)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	template< UINT cd, notify_handler_type func >
	struct notify_code_handler_hot
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) {
				lResult = (derived.*func)((int)wParam, (LPNMHDR)lParam, bHandled);
				return true;
			}
			return false;
		}
	};
	
	template< UINT cd, notify_handler_type func >
	struct notify_code_handler_not
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) {
				lResult = (derived.*func)((int)wParam, (LPNMHDR)lParam, bHandled);
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// notify_range_handler
	//
	template< UINT idFirst, UINT idLast, notify_handler_type func >
	struct notify_range_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) {
				bHandled = TRUE;
				lResult = (derived.*func)((int)wParam, (LPNMHDR)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// notify_range_code_handler
	//
	template< UINT idFirst, UINT idLast, UINT cd, notify_handler_type func >
	struct notify_range_code_handler
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_NOTIFY &&  cd == ((LPNMHDR)lParam)->code && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) {
				bHandled = TRUE;
				lResult = (derived.*func)((int)wParam, (LPNMHDR)lParam, bHandled);
				if (bHandled)
					return true;
			}
			return false;
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// chain_msg_map
	//
	//  Note: to avoid ::type syntax
	//
	template< class theChainClassOrMessageMap >
	struct chain_msg_map_base
	{
		template< class MessageMap >
		struct chain_msg_map_statement
		{
			static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
			{
				KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

				return derived.process_window_message<MessageMap>(KETCHUP_PROCESS_WINDOW_MESSAGE_ARGS);
			}

			typedef chain_msg_map_statement type;
		};

		template< class theChainClass >
		struct chain_atl_msg_map_statement
		{
			static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
			{
				KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();
				// no warning cast from BOOL to bool
				if (derived.theChainClass::ProcessWindowMessage(KETCHUP_PROCESS_WINDOW_MESSAGE_ARGS))
					return true;
				return false;
			}

			typedef chain_atl_msg_map_statement type;
		};

		typedef typename boost::mpl::eval_if< boost::mpl::is_sequence<theChainClassOrMessageMap>,
			chain_msg_map_statement<boost::mpl::_>,
			chain_atl_msg_map_statement<boost::mpl::_>
		>::type statement;

		typedef typename boost::mpl::apply<statement, theChainClassOrMessageMap>::type type;
	};

	template< class theChainClassOrMessageMap >
	struct chain_msg_map : chain_msg_map_base<theChainClassOrMessageMap>::type
	{
		/*
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (derived.theChainClass::ProcessWindowMessage(KETCHUP_PROCESS_WINDOW_MESSAGE_ARGS))
				return true;
			return false;
		}
		*/
	};

	///////////////////////////////////////////////////////////////////////////////
	// alt_msg_map
	//
	template< class MessageMap, DWORD msgMapID >
	struct alt_msg_map
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (dwMsgMapID == msgMapID) {
				return derived.process_window_message<MessageMap>(KETCHUP_PROCESS_WINDOW_MESSAGE_ARGS);
			}
			else
				return false; // pass to other alternates
		}
	};

	///////////////////////////////////////////////////////////////////////////////
	// cracked_handlers
	//
	template<
		void (Derived::*func)()
	>
	struct msg_wm_close
	{
		static bool compile(Derived& derived, KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_AS_PARAMS)
		{
			BOOST_STATIC_ASSERT(has_cracked_handlers);
			KETCHUP_PROCESS_WINDOW_MESSAGE_VARS_IGNORE_UNUSED_WARNING();

			if (uMsg == WM_CLOSE) {
				derived.set_msg_handled(true);
				(derived.*func)();
				lResult = 0;
				if (derived.is_msg_handled())
					return true;
			}
			return false;
		}
	};
//#include "detail/cracked_handlers.ipp"

}; // struct message_listener

} // namespace ketchup


#endif