#ifndef SUPPORT_EXCEPTION_HPP
#define SUPPORT_EXCEPTION_HPP

#include <exception>

#include "reader/support_string.hpp"
#include "reader/tchar.h"

#ifdef UNIX
#include <cstdio>
#endif

namespace cpro {
    class CryptoAPIException : public std::exception
    {
    public:
        explicit CryptoAPIException(DWORD err)
            : std::exception()
            , m_code(err)
            , m_message(format_err(err))
        {}
        CryptoAPIException(const CryptoAPIException& other)
            : std::exception()
            , m_code(other.m_code)
            , m_message(other.m_message)
        {}
        virtual ~CryptoAPIException() throw () {}

        DWORD getCode() const { return m_code; }
        virtual const char* what() const throw() { return ""; }
        virtual const TCHAR* twhat() const { return m_message.c_str(); }

    protected:
        DWORD m_code;
        ::tstring m_message;
        static ::tstring format_err(DWORD err);

        CryptoAPIException& operator=(const CryptoAPIException&);
    };

    SUP_INLINE ::tstring CryptoAPIException::format_err(DWORD err)
    {
        TCHAR message[512];
        size_t message_len;
        _stprintf(message, _TEXT("Error code 0x%x: "), err);
        message_len = _tcslen(message);
        if (!FormatMessage(
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            err,
            0,
            message + message_len,
            (DWORD)(_countof(message) - message_len),
            NULL))
        {
            _stprintf(message, _TEXT("Error code 0x%x."), err);
        }
        return ::tstring(message);
    }
} // namespace cpro

#endif // SUPPORT_EXCEPTION_HPP
