/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_fpicker.hxx"

//------------------------------------------------------------------------
// includes
//------------------------------------------------------------------------
#include <osl/diagnose.h>
#include "asynceventnotifier.hxx"
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>

#include <process.h>
#include <memory>
#include "SolarMutex.hxx"

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

using namespace com::sun::star;
using ::com::sun::star::ui::dialogs::XFilePickerListener;

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

CAsyncEventNotifier::CAsyncEventNotifier(cppu::OBroadcastHelper& rBroadcastHelper) :    
	m_hThread(0),
	m_bRun(false),
	m_ThreadId(0),
    m_rBroadcastHelper(rBroadcastHelper),
    m_NotifyEvent(m_hEvents[0]),
    m_ResumeNotifying(m_hEvents[1])
{    
    // m_NotifyEvent
    m_hEvents[0] = CreateEvent(0,		/* no security */
				               true,	/* manual reset */
				               false,	/* initial state not signaled */
				               0);	    /* automatic name */ 

    // m_ResumeNotifying				               
    m_hEvents[1] = CreateEvent(0,		/* no security */
				               true,	/* manual reset */
				               false,	/* initial state not signaled */
				               0);	    /* automatic name */    				        
}

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

CAsyncEventNotifier::~CAsyncEventNotifier()
{
	OSL_ENSURE(0 == m_hThread,"Thread not stopped, destroying this instance leads to desaster");
	
	CloseHandle(m_hEvents[0]);
	CloseHandle(m_hEvents[1]);
}

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

void SAL_CALL CAsyncEventNotifier::addListener(const uno::Type&                         aType    ,
                                               const uno::Reference< uno::XInterface >& xListener)
{
	if ( m_rBroadcastHelper.bDisposed )
		throw lang::DisposedException( 
			::rtl::OUString::createFromAscii( "FilePicker is already disposed" ),
			uno::Reference< uno::XInterface >() );

	if ( m_rBroadcastHelper.bInDispose )
		throw lang::DisposedException( 
			::rtl::OUString::createFromAscii( "FilePicker will be disposed now." ),
			uno::Reference< uno::XInterface >() );
    
	m_rBroadcastHelper.aLC.addInterface( aType, xListener );
}

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

void SAL_CALL CAsyncEventNotifier::removeListener(const uno::Type&                         aType    ,
                                                  const uno::Reference< uno::XInterface >& xListener)
{
	if ( m_rBroadcastHelper.bDisposed )
		throw lang::DisposedException( 
			::rtl::OUString::createFromAscii( "FilePicker is already disposed." ),
			uno::Reference< uno::XInterface >() );

	m_rBroadcastHelper.aLC.removeInterface( aType, xListener );
}

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

bool SAL_CALL CAsyncEventNotifier::startup(bool bCreateSuspended)
{
	osl::MutexGuard aGuard(m_Mutex);

	// m_bRun may already be false because of a
	// call to stop but the thread did not yet
	// terminate so m_hEventNotifierThread is
	// yet a valid thread handle that should 
	// not be overwritten
	if (!m_bRun)
	{
	    if (!bCreateSuspended)
	        SetEvent(m_ResumeNotifying);
	        
		m_hThread = (HANDLE)_beginthreadex(
			NULL, 0, CAsyncEventNotifier::ThreadProc, this, 0, &m_ThreadId);

		OSL_ASSERT(0 != m_hThread);

		if (m_hThread)
			m_bRun = true;
	}

	OSL_POSTCOND(m_bRun,"Could not start event notifier!");

	return m_bRun;
}

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

void SAL_CALL CAsyncEventNotifier::shutdown()
{
	unsigned nThreadId = GetCurrentThreadId();
	
	OSL_PRECOND(nThreadId != m_ThreadId, "Method called in wrong thread context!");

	osl::ResettableMutexGuard aGuard(m_Mutex);
	
	OSL_PRECOND(m_bRun,"Event notifier does not run!");

	m_bRun = false;
	m_EventList.clear();    
	
	// awake the the notifier thread
	SetEvent(m_ResumeNotifying);
	SetEvent(m_NotifyEvent);
		
	// releas the mutex here because the event
	// notifier thread may need it to finish
	aGuard.clear();

	// we are waiting infinite, so error will
	// be better detected in form of deadlocks
    if (WaitForSingleObject(m_hThread, INFINITE) == WAIT_FAILED) {
        OSL_ENSURE(false, "Waiting for thread termination failed!");
    }

	// lock mutex again to reset m_hThread
	// and prevent a race with start()
	aGuard.reset();
    
	CloseHandle(m_hThread);
	m_hThread = 0;
}

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

void CAsyncEventNotifier::suspend()
{
    ResetEvent(m_ResumeNotifying);
}
    
//------------------------------------------------
//
//------------------------------------------------

void CAsyncEventNotifier::resume()
{
    SetEvent(m_ResumeNotifying);
}
    
//------------------------------------------------
//
//------------------------------------------------

void SAL_CALL CAsyncEventNotifier::notifyEvent(CEventNotification* EventNotification)
{
	osl::MutexGuard aGuard(m_Mutex);

	OSL_ENSURE(m_bRun,"Event notifier is not running!");

	if (m_bRun)
	{
		m_EventList.push_back(EventNotification);
		SetEvent(m_NotifyEvent);
	}
}

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

size_t SAL_CALL CAsyncEventNotifier::getEventListSize()
{
	osl::MutexGuard aGuard(m_Mutex);
	return m_EventList.size();
}

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

void SAL_CALL CAsyncEventNotifier::resetNotifyEvent()
{
	osl::MutexGuard aGuard(m_Mutex);
	if (0 == m_EventList.size()) 		
		ResetEvent(m_NotifyEvent); 
}

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

CEventNotification* SAL_CALL CAsyncEventNotifier::getNextEventRecord()
{
	osl::MutexGuard aGuard(m_Mutex);
    return m_EventList.front();
}

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

void SAL_CALL CAsyncEventNotifier::removeNextEventRecord()
{
	osl::MutexGuard aGuard(m_Mutex);    
    m_EventList.pop_front();	
}

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

void SAL_CALL CAsyncEventNotifier::run() 
{
    while (m_bRun)
    {        
        WaitForMultipleObjects(2, m_hEvents, true, INFINITE);
        
        if (m_bRun)
	    {						
            while (getEventListSize() > 0)
            {                   
				std::auto_ptr<CEventNotification> EventNotification(getNextEventRecord());
                removeNextEventRecord();

			    ::cppu::OInterfaceContainerHelper* pICHelper = 
					m_rBroadcastHelper.getContainer(getCppuType((uno::Reference<XFilePickerListener>*)0));

			    if (pICHelper)
                {
                    ::cppu::OInterfaceIteratorHelper iter(*pICHelper);

			        while(iter.hasMoreElements())
			        {                				        
                        try
                        {								
							EventNotification->notifyEventListener(iter.next());
                        }
                        catch(uno::RuntimeException&)
                        {
                            OSL_ENSURE(sal_False,"RuntimeException during event dispatching");
                        }
                    }
                }

            } // while(getEventListSize() > 0)
			
			resetNotifyEvent();       

        } // if (m_bRun)		

    } // while(m_bRun)
}

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

unsigned int WINAPI CAsyncEventNotifier::ThreadProc(LPVOID pParam)
{
    CAsyncEventNotifier* pInst = reinterpret_cast< CAsyncEventNotifier* >(pParam);
    OSL_ASSERT(pInst);

    pInst->run();

    return 0;
}