/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



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

//------------------------------------------------------------------------
// includes
//------------------------------------------------------------------------
#include <osl/diagnose.h>
#include "WinClipbImpl.hxx"

#include <systools/win32/comtools.hxx>
#include "..\..\inc\DtObjFactory.hxx"
#include "..\dtobj\APNDataObject.hxx"
#include "WinClipboard.hxx"
#include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp>
#include "..\dtobj\XNotifyingDataObject.hxx"

#if defined _MSC_VER
#pragma warning(push,1)
#endif
#include <windows.h>
#include <ole2.h>
#include <objidl.h>
#if defined _MSC_VER
#pragma warning(pop)
#endif

//------------------------------------------------------------------------
// namespace directives
//------------------------------------------------------------------------

using namespace rtl;
using namespace osl;
using namespace std;
using namespace cppu;

using namespace com::sun::star::uno;
using namespace com::sun::star::datatransfer;
using namespace com::sun::star::datatransfer::clipboard;
using namespace com::sun::star::datatransfer::clipboard::RenderingCapabilities;

//------------------------------------------------------------------------
// deklarations
//------------------------------------------------------------------------

// definition of static members
CWinClipbImpl* CWinClipbImpl::s_pCWinClipbImpl = NULL;
osl::Mutex     CWinClipbImpl::s_aMutex;

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

CWinClipbImpl::CWinClipbImpl( const OUString& aClipboardName, CWinClipboard* theWinClipboard ) :
	m_itsName( aClipboardName ),	
	m_pWinClipboard( theWinClipboard ),
	m_pCurrentClipContent( NULL )
{
	OSL_ASSERT( NULL != m_pWinClipboard );

	// necessary to reassociate from
	// the static callback function
	s_pCWinClipbImpl = this;
	registerClipboardViewer( );
}

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

CWinClipbImpl::~CWinClipbImpl( )
{
	ClearableMutexGuard aGuard( s_aMutex );
	s_pCWinClipbImpl = NULL;
	aGuard.clear( );

	unregisterClipboardViewer( );
}

//------------------------------------------------------------------------
// getContent
//------------------------------------------------------------------------

Reference< XTransferable > SAL_CALL CWinClipbImpl::getContents( ) throw( RuntimeException )
{
    // use the shotcut or create a transferable from
    // system clipboard
    ClearableMutexGuard aGuard( m_ClipContentMutex );

	if ( NULL != m_pCurrentClipContent )
    {		
        return m_pCurrentClipContent->m_XTransferable;
    }

    // release the mutex, so that the variable may be
    // changed by other threads
    aGuard.clear( );

    Reference< XTransferable > rClipContent;

	// get the current dataobject from clipboard
	IDataObjectPtr pIDataObject;
	HRESULT hr = m_MtaOleClipboard.getClipboard( &pIDataObject );
        
    if ( SUCCEEDED( hr ) )
    {
	    // create an apartment neutral dataobject and initialize it with a 
		// com smart pointer to the IDataObject from clipboard
		IDataObjectPtr pIDo( new CAPNDataObject( pIDataObject ) );
	    
		CDTransObjFactory objFactory;

		// remeber pIDo destroys itself due to the smart pointer		
		rClipContent = objFactory.createTransferableFromDataObj( m_pWinClipboard->m_SrvMgr, pIDo );
    }
	
	return rClipContent;
}

//------------------------------------------------------------------------
// setContent
//------------------------------------------------------------------------

void SAL_CALL CWinClipbImpl::setContents( 
	const Reference< XTransferable >& xTransferable,
	const Reference< XClipboardOwner >& xClipboardOwner ) 
	throw( RuntimeException )
{	
	CDTransObjFactory objFactory;
	IDataObjectPtr    pIDataObj;
	
	if ( xTransferable.is( ) )
	{
        ClearableMutexGuard aGuard( m_ClipContentMutex );

		m_pCurrentClipContent = new CXNotifyingDataObject( 
			objFactory.createDataObjFromTransferable( m_pWinClipboard->m_SrvMgr , xTransferable ),
			xTransferable,
			xClipboardOwner,
			this );
        
        aGuard.clear( );

		pIDataObj = IDataObjectPtr( m_pCurrentClipContent );		
	}
	
	m_MtaOleClipboard.setClipboard(pIDataObj.get());	
}

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

OUString SAL_CALL CWinClipbImpl::getName(  ) throw( RuntimeException )
{
	return m_itsName;
}

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

sal_Int8 SAL_CALL CWinClipbImpl::getRenderingCapabilities(  ) throw( RuntimeException )
{
	return ( Delayed | Persistant );
}

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

void SAL_CALL CWinClipbImpl::flushClipboard( ) throw( RuntimeException )
{	
    // sollte eigentlich hier stehen: ClearableMutexGuard aGuard( m_ClipContentMutex );
    // geht aber nicht, da FlushClipboard zurückruft und das DataObject
    // freigibt und damit würde es einen Deadlock in onReleaseDataObject geben
    // FlushClipboard muß synchron sein, damit das runterfahren ggf. erst weitergeht,
    // wenn alle Clipboard-Formate gerendert wurden
    // die Abfrage ist nötig, damit nur geflusht wird, wenn wir wirklich Clipboardowner
    // sind (ich weiss nicht genau was passiert, wenn man flusht und nicht Clipboard
    // owner ist).
    // eventuell kann man aber die Abfrage in den Clipboard STA Thread verlagern, indem
    // man sich dort das DataObject merkt und vor dem flushen OleIsCurrentClipboard ruft

	if ( NULL != m_pCurrentClipContent )
		m_MtaOleClipboard.flushClipboard( );
}	

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

void SAL_CALL CWinClipbImpl::registerClipboardViewer( )
{
	m_MtaOleClipboard.registerClipViewer( CWinClipbImpl::onClipboardContentChanged );
}

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

void SAL_CALL CWinClipbImpl::unregisterClipboardViewer( )
{
	m_MtaOleClipboard.registerClipViewer( NULL );
}

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

void SAL_CALL CWinClipbImpl::dispose() throw( RuntimeException )
{
	OSL_ENSURE( !m_pCurrentClipContent, "Clipboard was not flushed before shutdown!" );
}

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

void WINAPI CWinClipbImpl::onClipboardContentChanged( void ) 
{
	MutexGuard aGuard( s_aMutex );

	// reassocition to instance through static member
	if ( NULL != s_pCWinClipbImpl )
		s_pCWinClipbImpl->m_pWinClipboard->notifyAllClipboardListener( );
}

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

void SAL_CALL CWinClipbImpl::onReleaseDataObject( CXNotifyingDataObject* theCaller )
{
	OSL_ASSERT( NULL != theCaller );

	if ( theCaller )
		theCaller->lostOwnership( );

	// if the current caller is the one we currently
	// hold, then set it to NULL because an external
	// source must be the clipboardowner now
    MutexGuard aGuard( m_ClipContentMutex );

	if ( m_pCurrentClipContent == theCaller )
		m_pCurrentClipContent = NULL;
}