///////////////////////////////////////////////////////////////////////////////
// Name:        src/msw/evtloopconsole.cpp
// Purpose:     wxConsoleEventLoop class for Windows
// Author:      Vadim Zeitlin
// Created:     01.06.01
// Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"


#ifndef WX_PRECOMP
    #include "wx/log.h"
    #include "wx/msw/private.h"
#endif //WX_PRECOMP

#include "wx/evtloop.h"

// ============================================================================
// wxMSWEventLoopBase implementation
// ============================================================================

// ----------------------------------------------------------------------------
// ctor/dtor
// ----------------------------------------------------------------------------

wxMSWEventLoopBase::wxMSWEventLoopBase()
{
    m_shouldExit = false;
    m_exitcode = 0;

    // Create initially not signalled auto-reset event object.
    m_heventWake = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
    if ( !m_heventWake )
        wxLogLastError(wxS("CreateEvent(wake)"));
}

wxMSWEventLoopBase::~wxMSWEventLoopBase()
{
    if ( m_heventWake && !::CloseHandle(m_heventWake) )
        wxLogLastError(wxS("CloseHandle(wake)"));
}

// ----------------------------------------------------------------------------
// wxEventLoop message processing dispatching
// ----------------------------------------------------------------------------

bool wxMSWEventLoopBase::Pending() const
{
    MSG msg;
    return ::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE) != 0;
}

void wxMSWEventLoopBase::WakeUp()
{
    if ( !::SetEvent(m_heventWake) )
        wxLogLastError(wxS("SetEvent(wake)"));
}

bool wxMSWEventLoopBase::MSWIsWakeUpRequested()
{
    return ::WaitForSingleObject(m_heventWake, 0) == WAIT_OBJECT_0;
}

#if wxUSE_THREADS

WXDWORD wxMSWEventLoopBase::MSWWaitForThread(WXHANDLE hThread)
{
    // The order is important here, the code using this function assumes that
    // WAIT_OBJECT_0 indicates the thread termination and anything else -- the
    // availability of an input event. So the thread handle must come first.
    HANDLE handles[2] = { hThread, m_heventWake };
    return ::MsgWaitForMultipleObjects
             (
               WXSIZEOF(handles),   // number of objects to wait for
               handles,             // the objects
               false,               // wait for any objects, not all
               INFINITE,            // no timeout
               QS_ALLINPUT |        // return as soon as there are any events
               QS_ALLPOSTMESSAGE
             );
}

#endif // wxUSE_THREADS

bool wxMSWEventLoopBase::GetNextMessage(WXMSG* msg)
{
    return GetNextMessageTimeout(msg, INFINITE) == TRUE;
}

int wxMSWEventLoopBase::GetNextMessageTimeout(WXMSG *msg, unsigned long timeout)
{
    // MsgWaitForMultipleObjects() won't notice any input which was already
    // examined (e.g. using PeekMessage()) but not yet removed from the queue
    // so we need to remove any immediately messages manually
    while ( !::PeekMessage(msg, 0, 0, 0, PM_REMOVE) )
    {
        DWORD rc = ::MsgWaitForMultipleObjects
                     (
                        1, &m_heventWake,
                        FALSE,
                        timeout,
                        QS_ALLINPUT | QS_ALLPOSTMESSAGE
                     );

        switch ( rc )
        {
            default:
                wxLogDebug("unexpected MsgWaitForMultipleObjects() return "
                           "value %lu", rc);
                wxFALLTHROUGH;

            case WAIT_TIMEOUT:
                return -1;

            case WAIT_OBJECT_0:
                // We were woken up by a background thread, which means there
                // is no actual input message available, but we should still
                // return to the event loop, so pretend there was WM_NULL in
                // the queue.
                wxZeroMemory(*msg);
                return TRUE;

            case WAIT_OBJECT_0 + 1:
                // Some message is supposed to be available, but spurious
                // wake ups are also possible, so just return to the loop:
                // either we'll get the message or start waiting again.
                break;
        }
    }

    return msg->message != WM_QUIT;
}

// ============================================================================
// wxConsoleEventLoop implementation
// ============================================================================

#if wxUSE_CONSOLE_EVENTLOOP

void wxConsoleEventLoop::ProcessMessage(WXMSG *msg)
{
    ::DispatchMessage(msg);
}

bool wxConsoleEventLoop::Dispatch()
{
    MSG msg;
    if ( !GetNextMessage(&msg) )
        return false;

    ProcessMessage(&msg);

    return !m_shouldExit;
}

int wxConsoleEventLoop::DispatchTimeout(unsigned long timeout)
{
    MSG msg;
    int rc = GetNextMessageTimeout(&msg, timeout);
    if ( rc != 1 )
        return rc;

    ProcessMessage(&msg);

    return !m_shouldExit;
}

void wxConsoleEventLoop::DoYieldFor(long eventsToProcess)
{
    wxEventLoopBase::DoYieldFor(eventsToProcess);
}

#endif // wxUSE_CONSOLE_EVENTLOOP
