#ifndef SNARL_INTERFACE_V41
#define SNARL_INTERFACE_V41

#include <tchar.h>
#include <windows.h>
#include <cstdio>

#ifndef SMTO_NOTIMEOUTIFNOTHUNG
	#define SMTO_NOTIMEOUTIFNOTHUNG 8
#endif


namespace Snarl {
	namespace V41 {

	static const LPCTSTR SnarlWindowClass = _T("w>Snarl");
	static const LPCTSTR SnarlWindowTitle = _T("Snarl");

	static const LPCTSTR SnarlGlobalMsg = _T("SnarlGlobalEvent");
	static const LPCTSTR SnarlAppMsg = _T("SnarlAppMessage");

	static const int SnarlPacketDataSize = 4096;

	// Enums put in own namespace, because ANSI C++ doesn't decorate enums with tagname :(
	namespace SnarlEnums {

		/// <summary>
		/// Global event identifiers.
		/// Identifiers marked with a '*' are sent by Snarl in two ways:
		///   1. As a broadcast message (uMsg = 'SNARL_GLOBAL_MSG')
		///   2. To the window registered in snRegisterConfig() or snRegisterConfig2()
		///      (uMsg = reply message specified at the time of registering)
		/// In both cases these values appear in wParam.
		///   
		/// Identifiers not marked are not broadcast; they are simply sent to the application's registered window.
		/// </summary>
		enum GlobalEvent
		{
			SnarlLaunched = 1,      // Snarl has just started running*
			SnarlQuit = 2,          // Snarl is about to stop running*
			SnarlAskAppletVer = 3,  // (R1.5) Reserved for future use
			SnarlShowAppUi = 4      // (R1.6) Application should show its UI
		};

		/// <summary>
		/// Message event identifiers.
		/// These are sent by Snarl to the window specified in RegisterApp() when the
		/// Snarl Notification raised times out or the user clicks on it.
		/// </summary>
		enum MessageEvent
		{
			NotificationClicked = 32,      // Notification was right-clicked by user
			NotificationCancelled = 32,    // Added in V37 (R1.6) -- same value, just improved the meaning of it
			NotificationTimedOut = 33,     // 
			NotificationAck = 34,          // Notification was left-clicked by user
			NotificationMenu = 35,         // Menu item selected (V39)
			NotificationMiddleButton = 36, // Notification middle-clicked by user (V39)
			NotificationClosed = 37        // User clicked the close gadget (V39)
		};

		/// <summary>
		/// Error values returned by calls to GetLastError().
		/// </summary>
		enum SnarlStatus
		{
			Success = 0,

			ErrorFailed = 101,        // miscellaneous failure
			ErrorUnknownCommand,      // specified command not recognised
			ErrorTimedOut,            // Snarl took too long to respond

			ErrorArgMissing = 109,    // required argument missing
			ErrorSystem,              // internal system error

			ErrorNotRunning = 201,    // Snarl handling window not found
			ErrorNotRegistered,       // 
			ErrorAlreadyRegistered,   // not used yet; RegisterApp() returns existing token
			ErrorClassAlreadyExists,  // not used yet; AddClass() returns existing token
			ErrorClassBlocked,
			ErrorClassNotFound,
			ErrorNotificationNotFound
		};

		/// <summary>
		/// Application flags - features this app supports.
		/// </summary>
		enum AppFlags
		{
			AppDefault = 0,
			AppHasPrefs = 1,
			AppHasAbout = 2,
			AppIsWindowless = 0x8000
		};

		enum SnarlCommand
		{
			RegisterApp = 1,
			UnregisterApp,
			UpdateApp,
			SetCallback,
			AddClass,
			RemoveClass,
			Notify,
			UpdateNotification,
			HideNotification,
			IsNotificationVisible,
			LastError                  // deprecated but retained for backwards compatability
		};
	}

	struct SnarlMessage
	{
		SnarlEnums::SnarlCommand Command;
		LONG32 Token;
		BYTE PacketData[SnarlPacketDataSize];
	};

	static const DWORD WM_SNARLTEST = WM_USER + 237;

	
	// ------------------------------------------------------------------------
	/// SnarlInterface class definition
	// ------------------------------------------------------------------------
	class SnarlInterface {
		public:
			SnarlInterface();
			~SnarlInterface();
			
			LPTSTR AllocateString(size_t n) { return new TCHAR[n]; }
			void FreeString(LPTSTR str)     { delete [] str; str = NULL; }
			void FreeString(LPCTSTR str)    { delete [] str; }

			/// <summary>Register application with Snarl.</summary>
			/// <returns>The application token or 0 on failure.</returns>
			/// <remarks>The application token is saved in SnarlInterface member variable, so just use return value to check for error.</remarks>
			LONG32 RegisterApp(LPCSTR signature, LPCSTR title, LPCSTR icon, HWND hWndReply = NULL, LONG32 msgReply = 0, SnarlEnums::AppFlags flags = SnarlEnums::AppDefault);
			LONG32 RegisterApp(LPCWSTR  signature, LPCWSTR title, LPCWSTR icon, HWND hWndReply = NULL, LONG32 msgReply = 0, SnarlEnums::AppFlags flags = SnarlEnums::AppDefault);

			/// <summary>Unregister application with Snarl when application is closing.</summary>
			/// <returns>0 on failure.</returns>
			LONG32 UnregisterApp();

			/// <summary>Update information provided when calling RegisterApp.</summary>
			/// <returns>0 on failure.</returns>
			LONG32 UpdateApp(LPCSTR title = NULL, LPCSTR icon = NULL);
			LONG32 UpdateApp(LPCWSTR title = NULL, LPCWSTR icon = NULL);

			/// <summary>Add a notification class to Snarl.</summary>
			/// <returns>0 on failure.</returns>
			LONG32 AddClass(LPCSTR className, LPCSTR description, bool enabled = true);
			LONG32 AddClass(LPCWSTR className, LPCWSTR description, bool enabled = true);
			
			/// <summary>Remove a notification class added with AddClass().</summary>
			/// <returns>0 on failure.</returns>
			LONG32 RemoveClass(LPCSTR className, bool forgetSettings = false);
			LONG32 RemoveClass(LPCWSTR className, bool forgetSettings = false);

			/// <summary>Remove all notification classes in one call.</summary>
			/// <returns>0 on failure.</returns>
			LONG32 RemoveAllClasses(bool forgetSettings = false);
			
			/// <summary>Show a Snarl notification.</summary>
			/// <returns>Returns the notification token or 0 on failure.</returns>
			/// <remarks>You can use <see cref="GetLastMsgToken()" /> to get the last token.</remarks>
			LONG32 EZNotify(LPCSTR className, LPCSTR title, LPCSTR text, LONG32 timeout = -1, LPCSTR icon = NULL, LONG32 priority = 0, LPCSTR acknowledge = NULL, LPCSTR value = NULL);
			LONG32 EZNotify(LPCWSTR className, LPCWSTR title, LPCWSTR text, LONG32 timeout = -1, LPCWSTR icon = NULL, LONG32 priority = 0, LPCWSTR acknowledge = NULL, LPCWSTR value = NULL);

			/// <summary>
			///     Show a Snarl notification.
			///     This function requires that you write your own packet data.
			/// </summary>
			/// <returns>Returns the notification token or 0 on failure.</returns>
			/// <remarks>You can use <see cref="GetLastMsgToken()" /> to get the last token.</remarks>
			LONG32 Notify(LPCSTR className, LPCSTR packetData);
			LONG32 Notify(LPCWSTR className, LPCWSTR packetData);

			/// <summary>Update the text or other parameters of a visible Snarl notification.</summary>
			/// <returns>0 on failure.</returns>
			LONG32 EZUpdate(LONG32 msgToken, LPCSTR title = NULL, LPCSTR text = NULL, LONG32 timeout = -1, LPCSTR icon = NULL);
			LONG32 EZUpdate(LONG32 msgToken, LPCWSTR title = NULL, LPCWSTR text = NULL, LONG32 timeout = -1, LPCWSTR icon = NULL);
			
			/// <summary>
			///     Update the text or other parameters of a visible Snarl notification.
			///     This function requires that you write your own packet data.
			/// </summary>
			/// <returns>0 on failure.</returns>
			LONG32 Update(LONG32 msgToken, LPCSTR packetData);
			LONG32 Update(LONG32 msgToken, LPCWSTR packetData);

			/// <summary>Hide a Snarl notification.</summary>
			/// <returns>0 on failure.</returns>
			LONG32 Hide(LONG32 msgToken);

			/// <summary>Test if a Snarl notification is visible.</summary>
			/// <returns>Returns -1 if message is visible. 0 if not visible or if an error occured.</returns>
			LONG32 IsVisible(LONG32 msgToken);

			/// <summary>Get the last error from Snarl. Call after other functions return 0 to know why it failed.</summary>
			/// <returns>Returns one of the SnarlEnums::SnarlStatus values.</returns>
			SnarlEnums::SnarlStatus GetLastError();

			/// <summary>Get Snarl version, if it is running.</summary>
			/// <returns>Returns a number indicating Snarl version.</returns>
			LONG32 GetVersion();

			/// <summary>
			///     Get the path to where Snarl is installed.
			///     ** Remember to call <see cref="FreeString(LPCTSTR)" /> on the returned string !!!
			/// </summary>
			/// <returns>Returns the path to where Snarl is installed.</returns>
			/// <remarks>This is a V39 API method.</remarks>
			LPCTSTR  GetAppPath();

			/// <summary>
			///     Get the path to where the default Snarl icons are located.
			///     <para>** Remember to call <see cref="FreeString(LPCTSTR)" /> on the returned string !!!</para>
			/// </summary>
			/// <returns>Returns the path to where the default Snarl icons are located.</returns>
			/// <remarks>This is a V39 API method.</remarks>
			LPCTSTR  GetIconsPath();

			/// <summary>GetLastMsgToken() returns token of the last message sent to Snarl.</summary>
			/// <returns>Returns message token of last message.</returns>
			/// <remarks>This function is not in the official API!</remarks>
			LONG32 GetLastMsgToken() const;
			
			/// <summary>Check whether Snarl is running</summary>
			/// <returns>Returns true if Snarl system was found running.</returns>
			static BOOL IsSnarlRunning();

			/// <summary>
			///     Returns the value of Snarl's global registered message.
			///     Notes:
			///       Snarl registers SNARL_GLOBAL_MSG during startup which it then uses to communicate
			///       with all running applications through a Windows broadcast message. This function can
			///       only fail if for some reason the Windows RegisterWindowMessage() function fails
			///       - given this, this function *cannnot* be used to test for the presence of Snarl.
			/// </summary>
			/// <returns>A 16-bit value (translated to 32-bit) which is the registered Windows message for Snarl.</returns>
			static UINT Broadcast();

			/// <summary>Returns the global Snarl Application message  (V39)</summary>
			/// <returns>Returns Snarl application registered message.</returns>
			static UINT AppMsg();

			/// <summary>Returns a handle to the Snarl Dispatcher window  (V37)</summary>
			/// <returns>Returns handle to Snarl Dispatcher window, or zero if it's not found.</returns>
			/// <remarks>This is now the preferred way to test if Snarl is actually running.</remarks>
			static HWND GetSnarlWindow();
		
		private:
			/// <summary>Send message to Snarl.</summary>
			/// <returns>Return zero on failure.</returns>
			LONG32 Send(SnarlMessage msg);

			/// <summary>Convert a unicode string to UTF8</summary>
			/// <returns>Returns pointer to the new string - Remember to delete [] returned string !</returns>
			/// <remarks>Remember to delete [] returned string !!!</remarks>
			LPSTR  WideToUTF8(LPCWSTR szWideStr);

			/// <summary>Pack data into the PackedData member field.</summary>
			/// <param name="data">Should always be a pointer to the PackedData field</param>
			/// <param name="format">The format string. Can be NULL or "" to just zero PackedData!</param>
			/// <param name="...">Variable number of objects to convert</param>
			void   PackData(BYTE* data, LPCSTR format, ...);

			LONG32 appToken;
			LONG32 lastMsgToken;
			SnarlEnums::SnarlStatus localError;

	}; // class

	} // namespace V41
} // namespace Snarl

#endif // SNARL_INTERFACE_V41