#ifndef SNARL_INTERFACE_V42_H
#define SNARL_INTERFACE_V42_H

#if defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API)
#define MINGW_HAS_SECURE_API 1
#endif

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

#ifndef SMTO_NOTIMEOUTIFNOTHUNG
#define SMTO_NOTIMEOUTIFNOTHUNG 8
#endif

namespace Snarl
{
namespace V42
{

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

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

static const DWORD WM_SNARLTEST = WM_USER + 237;

/// <summary>Application requests - these values appear in wParam.<para>Application should launch its settings UI</para></summary>
static const WPARAM AppDoPrefs = 1;
/// <summary>Application requests - these values appear in wParam.<para>Application should show its About... dialog</para></summary>
static const WPARAM AppDoAbout = 2;

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

/// <summary>
/// Global event identifiers - sent as Windows broadcast messages.
/// These values appear in wParam of the message.
/// </summary>
enum GlobalEvent {
    SnarlLaunched = 1,      // Snarl has just started running
    SnarlQuit = 2,          // Snarl is about to stop running
    SnarlGlobalStopped = 3, // Sent when stopped by user - Also sent to registered window
    SnarlGlobalStarted = 4, // Sent when started by user - Also sent to registered window
};

enum SnarlStatus {
    Success = 0,

    // Snarl-Stopped/Started/UserAway/UserBack is defined in the GlobalEvent struct in VB6 code,
    // but are sent directly to a registered window, so in C# they are defined here instead.
    // Implemented as of Snarl R2.4 Beta3
    SnarlStopped = 3,              // Sent when stopped by user - Also sent as broadcast message
    SnarlStarted,                  // Sent when started by user - Also sent as broadcast message
    SnarlUserAway,                 // Away mode was enabled
    SnarlUserBack,                 // Away mode was disabled

    // Win32 callbacks (renamed under V42)
    CallbackRightClick = 32,       // Deprecated as of V42, ex. SNARL_NOTIFICATION_CLICKED/SNARL_NOTIFICATION_CANCELLED
    CallbackTimedOut,
    CallbackInvoked,               // left clicked and no default callback assigned
    CallbackMenuSelected,          // HIWORD(wParam) contains 1-based menu item index
    CallbackMiddleClick,           // Deprecated as of V42
    CallbackClosed,

    // critical errors
    ErrorFailed = 101,             // miscellaneous failure
    ErrorUnknownCommand,           // specified command not recognised
    ErrorTimedOut,                 // Snarl took too long to respond
    //104 gen critical #4
    //105 gen critical #5
    ErrorBadSocket = 106,          // invalid socket (or some other socket-related error)
    ErrorBadPacket = 107,          // badly formed request
    ErrorInvalidArg = 108,         // arg supplied was invalid (Added in v42.56)
    ErrorArgMissing = 109,         // required argument missing
    ErrorSystem,                   // internal system error
    //120 libsnarl critical block
    ErrorAccessDenied = 121,       // libsnarl only
    //130 SNP/3.0-specific
    ErrorUnsupportedVersion = 131, // requested SNP version is not supported
    ErrorNoActionsProvided,        // empty request
    ErrorUnsupportedEncryption,    // requested encryption type is not supported
    ErrorUnsupportedHashing,       // requested message hashing type is not supported

    // warnings
    ErrorNotRunning = 201,         // Snarl handling window not found
    ErrorNotRegistered,
    ErrorAlreadyRegistered,        // not used yet; sn41RegisterApp() returns existing token
    ErrorClassAlreadyExists,       // not used yet
    ErrorClassBlocked,
    ErrorClassNotFound,
    ErrorNotificationNotFound,
    ErrorFlooding,                 // notification generated by same class within quantum
    ErrorDoNotDisturb,             // DnD mode is in effect was not logged as missed
    ErrorCouldNotDisplay,          // not enough space on-screen to display notification
    ErrorAuthFailure,              // password mismatch
    // Release 2.4.2
    ErrorDiscarded,                // discarded for some reason, e.g. foreground app match
    ErrorNotSubscribed,            // subscriber not found

    // informational
    // code 250 reserved for future use
    WasMerged = 251,               // notification was merged, returned token is the one we merged with

    // callbacks
    // code 300 reserved for future use
    NotifyGone = 301,              // reserved for future use

    // The following are currently specific to SNP 2.0 and are effectively the
    // Win32 SNARL_CALLBACK_nnn constants with 270 added to them

    // SNARL_NOTIFY_CLICK = 302    // indicates notification was right-clicked (deprecated as of V42)
    NotifyExpired = 303,
    NotifyInvoked = 304,           // note this was "ACK" in a previous life
    NotifyMenu,                    // indicates an item was selected from user-defined menu (deprecated as of V42)
    // SNARL_NOTIFY_EX_CLICK       // user clicked the middle mouse button (deprecated as of V42)
    NotifyClosed = 307,            // // user clicked the notification's close gadget (GNTP only)

    // the following is generic to SNP and the Win32 API
    NotifyAction = 308,             // user picked an action from the list, the data value will indicate which one

    // C++ interface custom errors- not part of official API!
    ErrorCppInterface = 1001
};

/// <summary>
/// The priority of messages.
/// See <cref>http://sourceforge.net/apps/mediawiki/snarlwin/index.php?title=Generic_API#notify</cref>
/// </summary>
enum MessagePriority {
    PriorityUndefined = -2,
    PriorityLow = -1,
    PriorityNormal = 0,
    PriorityHigh = 1
};

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

} // namespace SnarlEnums

template<class T>
class SnarlParameterList
{
public:
    typedef std::pair<std::basic_string<T>, std::basic_string<T> > PairType;

    SnarlParameterList()
    {
    }

    explicit SnarlParameterList(int initialCapacity)
    {
        list.reserve(initialCapacity);
    }

    void Add(const T *_key, const T *_value)
    {
        if (_value != NULL) {
            list.push_back(PairType(std::basic_string<T>(_key), std::basic_string<T>(_value)));    //
        }
    }

    void Add(const T *_key, LONG32 _value)
    {
        std::basic_stringstream<T> valStr;
        valStr << _value;
        list.push_back(PairType(std::basic_string<T>(_key), valStr.str()));
    }

    void Add(const T *_key, void *_value)
    {
        if (_value != NULL) {
            std::basic_stringstream<T> valStr;
            valStr << (INT_PTR)_value; // Uckly hack, to get stringstream to print void* as decimal not hex

            list.push_back(PairType(std::basic_string<T>(_key), valStr.str()));
        }
    }

    const std::vector<PairType> &GetList() const
    {
        return list;
    }

private:
    std::vector<PairType> list;
};

// ----------------------------------------------------------------------------------------
// SnarlInterface class definition
// ----------------------------------------------------------------------------------------
class SnarlInterface
{
public:
    /// <summary>Requests strings known by Snarl</summary>
    class Requests
    {
    public:
        static LPCSTR  AddActionA()
        {
            return  "addaction";
        }
        static LPCWSTR AddActionW()
        {
            return L"addaction";
        }
        static LPCSTR  AddClassA()
        {
            return  "addclass";
        }
        static LPCWSTR AddClassW()
        {
            return L"addclass";
        }
        static LPCSTR  ClearActionsA()
        {
            return  "clearactions";
        }
        static LPCWSTR ClearActionsW()
        {
            return L"clearactions";
        }
        static LPCSTR  ClearClassesA()
        {
            return  "clearclasses";
        }
        static LPCWSTR ClearClassesW()
        {
            return L"clearclasses";
        }
        static LPCSTR  HelloA()
        {
            return  "hello";
        }
        static LPCWSTR HelloW()
        {
            return L"hello";
        }
        static LPCSTR  HideA()
        {
            return  "hide";
        }
        static LPCWSTR HideW()
        {
            return L"hide";
        }
        static LPCSTR  IsVisibleA()
        {
            return  "isvisible";
        }
        static LPCWSTR IsVisibleW()
        {
            return L"isvisible";
        }
        static LPCSTR  NotifyA()
        {
            return  "notify";
        }
        static LPCWSTR NotifyW()
        {
            return L"notify";
        }
        static LPCSTR  RegisterA()
        {
            return  "reg";    // register
        }
        static LPCWSTR RegisterW()
        {
            return L"reg";
        }
        static LPCSTR  RemoveClassA()
        {
            return  "remclass";
        }
        static LPCWSTR RemoveClassW()
        {
            return L"remclass";
        }
        static LPCSTR  UnregisterA()
        {
            return  "unregister";
        }
        static LPCWSTR UnregisterW()
        {
            return L"unregister";
        }
        static LPCSTR  UpdateAppA()
        {
            return  "updateapp";
        }
        static LPCWSTR UpdateAppW()
        {
            return L"updateapp";
        }
        static LPCSTR  UpdateA()
        {
            return  "update";
        }
        static LPCWSTR UpdateW()
        {
            return L"update";
        }
        static LPCSTR  VersionA()
        {
            return  "version";
        }
        static LPCWSTR VersionW()
        {
            return L"version";
        }
    };

    SnarlInterface();
    virtual ~SnarlInterface();

    // ------------------------------------------------------------------------------------
    // Static functions
    // ------------------------------------------------------------------------------------

    // Use FreeString, when SnarlInterface returns a null terminated string pointer
    static LPTSTR AllocateString(size_t n)
    {
        return new TCHAR[n];
    }
    static void FreeString(LPSTR str)
    {
        delete [] str;
        str = NULL;
    }
    static void FreeString(LPCSTR str)
    {
        delete [] str;
    }
    static void FreeString(LPWSTR str)
    {
        delete [] str;
        str = NULL;
    }
    static void FreeString(LPCWSTR str)
    {
        delete [] str;
    }

    /// <summary>Send message to Snarl.</summary>
    /// <param name='request'>The request string. If using unicode version, the string will be UTF8 encoded before sending.</param>
    /// <param name='replyTimeout'>Time to wait before timeout - Default = 1000</param>
    /// <returns>
    ///   Return zero or positive on Success.
    ///   Negative on failure. (Get error code by abs(return_value))
    ///   <see>http://sourceforge.net/apps/mediawiki/snarlwin/index.php?title=Windows_API#Return_Value</see>
    /// </returns>
    static LONG32 DoRequest(LPCSTR request, UINT replyTimeout = 1000);
    static LONG32 DoRequest(LPCWSTR request, UINT replyTimeout = 1000);

    /// <summary>Escapes a string, so it can be passed to Snarl.</summary>
    /// <remarks>
    ///   Should only be used, if you are using DoRequest() and not the helper functions.
    ///   Remember to Escape each key/value pair individually.
    /// </remarks>
    static std::basic_string<char> &Escape(std::basic_string<char> &str);
    static std::basic_string<wchar_t> &Escape(std::basic_string<wchar_t> &str);

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

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

    /// <summary>
    ///     Get the path to where the default Snarl icons are located.
    ///     <para>** Remember to call <see cref="FreeString(LPSTR)" /> 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>
    static LPCTSTR GetIconsPath();

    /// <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();

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

    /// <summary>Check whether Snarl is running</summary>
    /// <returns>Returns true if Snarl system was found running.</returns>
    static BOOL IsSnarlRunning();

    // ------------------------------------------------------------------------------------

    /// <summary>Adds an action to an existing (on-screen or in the missed list) notification.</summary>
    LONG32 AddAction(LONG32 msgToken, LPCSTR  label, LPCSTR  cmd);
    LONG32 AddAction(LONG32 msgToken, LPCWSTR label, LPCWSTR cmd);

    /// <summary>Add a notification class to Snarl.</summary>
    LONG32 AddClass(LPCSTR classId, LPCSTR name, LPCSTR title = NULL, LPCSTR text = NULL, LPCSTR icon = NULL, LPCSTR sound = NULL, LONG32 duration = NULL, LPCSTR callback = NULL, bool enabled = true);
    LONG32 AddClass(LPCWSTR classId, LPCWSTR name, LPCWSTR title = NULL, LPCWSTR text = NULL, LPCWSTR icon = NULL, LPCWSTR sound = NULL, LONG32 duration = -1, LPCWSTR callback = NULL, bool enabled = true);

    /// <summary>Remove all notification classes in one call.</summary>
    LONG32 ClearActions(LONG32 msgToken);

    /// <summary>Remove all notification classes in one call.</summary>
    LONG32 ClearClasses();

    /// <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>Hide a Snarl notification.</summary>
    LONG32 Hide(LONG32 msgToken);

    /// <summary>Test if a Snarl notification is visible. If the message is visible the function returns SnarlEnums::Success.</summary>
    LONG32 IsVisible(LONG32 msgToken);

    /// <summary>Show a Snarl notification.</summary>
    /// <returns>Returns the notification token or negative on failure.</returns>
    /// <remarks>You can use <see cref="GetLastMsgToken()" /> to get the last token.</remarks>
    LONG32 Notify(LPCSTR classId = NULL, LPCSTR title = NULL, LPCSTR text = NULL, LONG32 timeout = -1, LPCSTR iconPath = NULL, LPCSTR iconBase64 = NULL, SnarlEnums::MessagePriority priority = SnarlEnums::PriorityUndefined, LPCSTR uid = NULL, LPCSTR callback = NULL, LPCSTR value = NULL);
    LONG32 Notify(LPCWSTR classId = NULL, LPCWSTR title = NULL, LPCWSTR text = NULL, LONG32 timeout = -1, LPCWSTR iconPath = NULL, LPCWSTR iconBase64 = NULL,  SnarlEnums::MessagePriority priority = SnarlEnums::PriorityUndefined, LPCWSTR uid = NULL, LPCWSTR callback = NULL, LPCWSTR value = NULL);

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

    /// <summary>Remove a notification class added with AddClass().</summary>
    LONG32 RemoveClass(LPCSTR classId);
    LONG32 RemoveClass(LPCWSTR classId);

    /// <summary>Update the text or other parameters of a visible Snarl notification.</summary>
    LONG32 Update(LONG32 msgToken, LPCSTR classId = NULL, LPCSTR title = NULL, LPCSTR text = NULL, LONG32 timeout = -1, LPCSTR iconPath = NULL, LPCSTR iconBase64 = NULL, SnarlEnums::MessagePriority priority = SnarlEnums::PriorityUndefined, LPCSTR callback = NULL, LPCSTR value = NULL);
    LONG32 Update(LONG32 msgToken, LPCWSTR classId = NULL, LPCWSTR title = NULL, LPCWSTR text = NULL, LONG32 timeout = -1, LPCWSTR iconPath = NULL, LPCWSTR iconBase64 = NULL, SnarlEnums::MessagePriority priority = SnarlEnums::PriorityUndefined, LPCWSTR callback = NULL, LPCWSTR value = NULL);

    /// <summary>Unregister application with Snarl when application is closing.</summary>
    LONG32 Unregister(LPCSTR signature);
    LONG32 Unregister(LPCWSTR signature);

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

private:

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

    static LONG32 DoRequest(LPCSTR request, SnarlParameterList<char> &spl, UINT replyTimeout = 1000);
    static LONG32 DoRequest(LPCWSTR request, SnarlParameterList<wchar_t> &spl, UINT replyTimeout = 1000);

    void SetPassword(LPCSTR password);
    void SetPassword(LPCWSTR password);
    void ClearPassword();

    LONG32 appToken;
    LONG32 lastMsgToken;
    LPSTR szPasswordA;
    LPWSTR szPasswordW;
}; // class SnarlInterface

} // namespace V42
} // namespace Snarl

#endif // SNARL_INTERFACE_V42_H

