#include <functional>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <utility>
#include <iostream>
#include <cstddef>

#define WINAPI
typedef size_t WPARAM;
typedef ptrdiff_t LPARAM;
typedef void * HANDLE;
typedef HANDLE HWND;
typedef ptrdiff_t LRESULT;
typedef uint32_t UINT;

LRESULT WINAPI DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    return 0;
}

class Win32MessageHandler {
public:
    typedef UINT HandlerId;
    
    typedef std::function<LRESULT (HWND, UINT, WPARAM, LPARAM, bool & handled)> Callback;
    static HandlerId RegisterHandler(UINT messageId, Callback const & callback);
    static HandlerId RegisterHandler(UINT messageId, Callback && callback);
    static void UnregisterHandler(HandlerId const & id);
    
    static LRESULT WINAPI WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
private:
    static std::unordered_map<uint32_t, Callback> s_messages;
};

std::unordered_map<uint32_t, Win32MessageHandler::Callback> Win32MessageHandler::s_messages;

Win32MessageHandler::HandlerId Win32MessageHandler::RegisterHandler(UINT messageId, Callback const & callback) {
    s_messages.insert(std::make_pair(messageId, callback));
    return messageId;
}

Win32MessageHandler::HandlerId Win32MessageHandler::RegisterHandler(UINT messageId, Callback && callback) {
    s_messages.insert(std::make_pair(messageId, std::move(callback)));
    return messageId;
}

void Win32MessageHandler::UnregisterHandler(HandlerId const & id) {
    s_messages.erase(id);
}

LRESULT WINAPI Win32MessageHandler::WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    auto itor = s_messages.find(msg);
    if(itor != s_messages.end()) {
        bool handled = true;
        auto result = itor->second(hwnd, msg, wparam, lparam, handled);
        if(handled)
            return result;
    }
    
    return DefWindowProc(hwnd, msg, wparam, lparam);
}

class SomeClass {
public:
    LRESULT SomeInstanceClassMessageHandlerFunction(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, bool & result) {
        std::cout<<"Crazy ass instance function!"<<std::endl;
        return 0;
    }
};

int main(int argc, char ** argv) {
    using namespace std::placeholders;
    
    auto handlerId = Win32MessageHandler::RegisterHandler(0, [](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, bool & handled) {
        std::cout<<"Hello world"<<std::endl;
        return 0;
    });
    
    SomeClass s;
    
    Win32MessageHandler::RegisterHandler(1,
        Win32MessageHandler::Callback(
            std::bind(
                &SomeClass::SomeInstanceClassMessageHandlerFunction,
                &s,
                _1,
                _2,
                _3,
                _4,
                _5
            )
        )
    );
    Win32MessageHandler::WindowProc(0, 0, 0, 0);
    Win32MessageHandler::WindowProc(0, 1, 0, 0);
    
    Win32MessageHandler::UnregisterHandler(handlerId);
    
    Win32MessageHandler::WindowProc(0, 0, 0, 0);
}