/*
 * ReactOS Explorer
 *
 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "precomp.h"

class CDesktopThread
{
private:
    CComPtr<ITrayWindow> m_Tray;
    HANDLE m_hInitEvent;
    HANDLE m_hThread;
    DWORD m_ThreadId;

    DWORD DesktopThreadProc();
    static DWORD WINAPI s_DesktopThreadProc(LPVOID lpParameter);

public:
    CDesktopThread();
    ~CDesktopThread();

    HRESULT Initialize(ITrayWindow* pTray);
    void Destroy();
};

/*******************************************************************/

CDesktopThread::CDesktopThread():
    m_Tray(NULL),
    m_hInitEvent(NULL),
    m_hThread(NULL),
    m_ThreadId(0)
{
}

CDesktopThread::~CDesktopThread()
{
    Destroy();
}

HRESULT CDesktopThread::Initialize(ITrayWindow* pTray)
{
    HANDLE Handles[2];

    if (!pTray || m_Tray)
    {
        return E_FAIL;
    }

    m_hInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    if (!m_hInitEvent)
    {
        return E_FAIL;
    }

    m_Tray = pTray;
    m_hThread = CreateThread(NULL, 0, s_DesktopThreadProc, (LPVOID)this, 0, &m_ThreadId);

    if (!m_hThread)
    {
        CloseHandle(m_hInitEvent);
        m_hInitEvent = NULL;

        m_Tray = NULL;

        return E_FAIL;
    }

    Handles[0] = m_hThread;
    Handles[1] = m_hInitEvent;

    for (;;)
    {
        DWORD WaitResult = MsgWaitForMultipleObjects(_countof(Handles), Handles, FALSE, INFINITE, QS_ALLEVENTS);

        if (WaitResult == WAIT_OBJECT_0 + _countof(Handles))
        {
            TrayProcessMessages(m_Tray);
        }
        else if (WaitResult != WAIT_FAILED && WaitResult != WAIT_OBJECT_0)
        {
            break;
        }
        else
        {
            CloseHandle(m_hThread);
            m_hThread = NULL;
            m_ThreadId = 0;

            CloseHandle(m_hInitEvent);
            m_hInitEvent = NULL;

            m_Tray = NULL;

            return E_FAIL;
        }
    }
    return S_OK;
}

void CDesktopThread::Destroy()
{
    if (m_hThread)
    {
        DWORD WaitResult = WaitForSingleObject(m_hThread, 0);
        if (WaitResult == WAIT_TIMEOUT)
        {
            /* Send WM_QUIT message to the thread and wait for it to terminate */
            PostThreadMessageW(m_ThreadId, WM_QUIT, 0, 0);
            WaitForSingleObject(m_hThread, INFINITE);
        }

        CloseHandle(m_hThread);
        m_hThread = NULL;
        m_ThreadId = 0;
    }

    if (m_hInitEvent)
    {
        CloseHandle(m_hInitEvent);
        m_hInitEvent = NULL;
    }

    m_Tray = NULL;
}

DWORD CDesktopThread::DesktopThreadProc()
{
    CComPtr<IShellDesktopTray> pSdt;
    HANDLE hDesktop;
    HRESULT hRet;
    DWORD dwResult = 1;

    OleInitialize(NULL);

    hRet = m_Tray->QueryInterface(IID_PPV_ARG(IShellDesktopTray, &pSdt));
    if (!SUCCEEDED(hRet))
    {
        goto Cleanup;
    }

    hDesktop = _SHCreateDesktop(pSdt);
    if (!hDesktop)
    {
        goto Cleanup;
    }

    if (!SetEvent(m_hInitEvent))
    {
        /* Failed to notify that we initialized successfully, kill ourselves
         * to make the main thread wake up! */
        goto Cleanup;
    }

    _SHDesktopMessageLoop(hDesktop);
    dwResult = 0;

Cleanup:
    OleUninitialize();
    return dwResult;
}

DWORD WINAPI CDesktopThread::s_DesktopThreadProc(LPVOID lpParameter)
{
    CDesktopThread* pDesktopThread = static_cast<CDesktopThread*>(lpParameter);
    return pDesktopThread->DesktopThreadProc();
}

/*******************************************************************/

HANDLE
DesktopCreateWindow(IN OUT ITrayWindow *Tray)
{
    CDesktopThread* pDesktopThread = new CDesktopThread();

    HRESULT hres = pDesktopThread->Initialize(Tray);
    if (FAILED_UNEXPECTEDLY(hres))
    {
        delete pDesktopThread;
        return NULL;
    }

    return pDesktopThread;
}

VOID
DesktopDestroyShellWindow(IN HANDLE hDesktop)
{
    CDesktopThread* pDesktopThread = reinterpret_cast<CDesktopThread*>(hDesktop);
    delete pDesktopThread;
}