/**************************************************************
 * 
 * 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_embeddedobj.hxx"
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <com/sun/star/embed/EmbedMapUnits.hpp>
#include <com/sun/star/embed/EmbedMisc.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>

#include <rtl/logfile.hxx>

#include <oleembobj.hxx>
#include <olecomponent.hxx>
#include <comphelper/mimeconfighelper.hxx>
#include <comphelper/seqstream.hxx>

using namespace ::com::sun::star;
using namespace ::comphelper;

embed::VisualRepresentation OleEmbeddedObject::GetVisualRepresentationInNativeFormat_Impl(
					const uno::Reference< io::XStream > xCachedVisRepr )
		throw ( uno::Exception )
{
	embed::VisualRepresentation aVisualRepr;

	// TODO: detect the format in the future for now use workaround
	uno::Reference< io::XInputStream > xInStream = xCachedVisRepr->getInputStream();
	uno::Reference< io::XSeekable > xSeekable( xCachedVisRepr, uno::UNO_QUERY );
	if ( !xInStream.is() || !xSeekable.is() )
		throw uno::RuntimeException();

	uno::Sequence< sal_Int8 > aSeq( 2 );
	xInStream->readBytes( aSeq, 2 );
	xSeekable->seek( 0 );
	if ( aSeq.getLength() == 2 && aSeq[0] == 'B' && aSeq[1] == 'M' )
	{
		// it's a bitmap
		aVisualRepr.Flavor = datatransfer::DataFlavor(
            ::rtl::OUString::createFromAscii( "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" ),
			::rtl::OUString::createFromAscii( "Bitmap" ),
			::getCppuType( (const uno::Sequence< sal_Int8 >*) NULL ) );
	}
	else
	{
		// it's a metafile
		aVisualRepr.Flavor = datatransfer::DataFlavor(
            ::rtl::OUString::createFromAscii( "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"" ),
			::rtl::OUString::createFromAscii( "Windows Metafile" ),
			::getCppuType( (const uno::Sequence< sal_Int8 >*) NULL ) );
	}

	sal_Int32 nStreamLength = (sal_Int32)xSeekable->getLength();
	uno::Sequence< sal_Int8 > aRepresent( nStreamLength );
	xInStream->readBytes( aRepresent, nStreamLength );
	aVisualRepr.Data <<= aRepresent;

	return aVisualRepr;
}

void SAL_CALL OleEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize )
		throw ( lang::IllegalArgumentException,
				embed::WrongStateException,
				uno::Exception,
				uno::RuntimeException )
{
	RTL_LOGFILE_CONTEXT( aLog, "embeddedobj (mv76033) OleEmbeddedObject::setVisualAreaSize" );

    // begin wrapping related part ====================
    uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
    if ( xWrappedObject.is() )
    {
        // the object was converted to OOo embedded object, the current implementation is now only a wrapper
        xWrappedObject->setVisualAreaSize( nAspect, aSize );
        return;
    }
    // end wrapping related part ====================

	::osl::ResettableMutexGuard aGuard( m_aMutex );
	if ( m_bDisposed )
		throw lang::DisposedException(); // TODO

	OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!\n" );
	if ( nAspect == embed::Aspects::MSOLE_ICON )
		// no representation can be retrieved
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "Illegal call!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

	if ( m_nObjectState == -1 )
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "The object is not loaded!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

#ifdef WNT
	// RECOMPOSE_ON_RESIZE misc flag means that the object has to be switched to running state on resize.
	// SetExtent() is called only for objects that require it,
	// it should not be called for MSWord documents to workaround problem i49369
	// If cached size is not set, that means that this is the size initialization, so there is no need to set the real size
	sal_Bool bAllowToSetExtent = 
	  ( ( getStatus( nAspect ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE )
	  && !MimeConfigurationHelper::ClassIDsEqual( m_aClassID, MimeConfigurationHelper::GetSequenceClassID( 0x00020906L, 0x0000, 0x0000,
	  													 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 ) ) 
	  && m_bHasCachedSize );

	if ( m_nObjectState == embed::EmbedStates::LOADED && bAllowToSetExtent )
	{
		aGuard.clear();
		try {
			changeState( embed::EmbedStates::RUNNING );
		}
		catch( uno::Exception& )
		{
			OSL_ENSURE( sal_False, "The object should not be resized without activation!\n" );
		}
		aGuard.reset();
	}

	if ( m_pOleComponent && m_nObjectState != embed::EmbedStates::LOADED && bAllowToSetExtent )
	{
		awt::Size aSizeToSet = aSize;
		aGuard.clear();
		try {
			m_pOleComponent->SetExtent( aSizeToSet, nAspect ); // will throw an exception in case of failure
			m_bHasSizeToSet = sal_False;
		}
		catch( uno::Exception& )
		{
			// some objects do not allow to set the size even in running state
			m_bHasSizeToSet = sal_True;
			m_aSizeToSet = aSizeToSet;
			m_nAspectToSet = nAspect;
		}
		aGuard.reset();
	}
#endif

	// cache the values
	m_bHasCachedSize = sal_True;
	m_aCachedSize = aSize;
	m_nCachedAspect = nAspect;
}

awt::Size SAL_CALL OleEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect )
		throw ( lang::IllegalArgumentException,
				embed::WrongStateException,
				uno::Exception,
				uno::RuntimeException )
{
	RTL_LOGFILE_CONTEXT( aLog, "embeddedobj (mv76033) OleEmbeddedObject::getVisualAreaSize" );

    // begin wrapping related part ====================
    uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
    if ( xWrappedObject.is() )
    {
        // the object was converted to OOo embedded object, the current implementation is now only a wrapper
        return xWrappedObject->getVisualAreaSize( nAspect );
    }
    // end wrapping related part ====================

	::osl::ResettableMutexGuard aGuard( m_aMutex );
	if ( m_bDisposed )
		throw lang::DisposedException(); // TODO

	OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!\n" );
	if ( nAspect == embed::Aspects::MSOLE_ICON )
		// no representation can be retrieved
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "Illegal call!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

	if ( m_nObjectState == -1 )
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "The object is not loaded!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

	awt::Size aResult;

#ifdef WNT
	// TODO/LATER: Support different aspects
	if ( m_pOleComponent && !m_bHasSizeToSet && nAspect == embed::Aspects::MSOLE_CONTENT )
	{
		try
		{
			// the cached size updated every time the object is stored
			if ( m_bHasCachedSize )
			{
				aResult = m_aCachedSize;
			}
			else
			{
				// there is no internal cache
				awt::Size aSize;
				aGuard.clear();
	
				sal_Bool bSuccess = sal_False;
				if ( getCurrentState() == embed::EmbedStates::LOADED )
				{
					OSL_ENSURE( sal_False, "Loaded object has no cached size!\n" );

					// try to switch the object to RUNNING state and request the value again
					try {
						changeState( embed::EmbedStates::RUNNING );
					}
					catch( uno::Exception )
					{
						throw embed::NoVisualAreaSizeException(
								::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No size available!\n" ) ),
								uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );
					}
				}

				try
				{
					// first try to get size using replacement image
					aSize = m_pOleComponent->GetExtent( nAspect ); // will throw an exception in case of failure
					bSuccess = sal_True;
				}
				catch( uno::Exception& )
				{
				}

				if ( !bSuccess )
				{
					try
					{
						// second try the cached replacement image
						aSize = m_pOleComponent->GetCachedExtent( nAspect ); // will throw an exception in case of failure
						bSuccess = sal_True;
					}
					catch( uno::Exception& )
					{
					}
				}

				if ( !bSuccess )
				{
					try
					{
						// third try the size reported by the object
						aSize = m_pOleComponent->GetReccomendedExtent( nAspect ); // will throw an exception in case of failure
						bSuccess = sal_True;
					}
					catch( uno::Exception& )
					{
					}
				}

				if ( !bSuccess )
					throw embed::NoVisualAreaSizeException(
									::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No size available!\n" ) ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

				aGuard.reset();
				
				m_aCachedSize = aSize;
				m_nCachedAspect = nAspect;
				m_bHasCachedSize = sal_True;

				aResult = m_aCachedSize;
			}
		}
		catch ( embed::NoVisualAreaSizeException& )
		{
			throw;
		}
		catch ( uno::Exception& )
		{
			throw embed::NoVisualAreaSizeException(
							::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No size available!\n" ) ),
							uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );
		}
	}
	else
#endif
	{
		// return cached value
		if ( m_bHasCachedSize )
		{
			OSL_ENSURE( nAspect == m_nCachedAspect, "Unexpected aspect is requested!\n" );
			aResult = m_aCachedSize;
		}
		else
		{
			throw embed::NoVisualAreaSizeException(
							::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No size available!\n" ) ),
							uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );
		}
	}

	return aResult;
}

embed::VisualRepresentation SAL_CALL OleEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect )
		throw ( lang::IllegalArgumentException,
				embed::WrongStateException,
				uno::Exception,
				uno::RuntimeException )
{
	RTL_LOGFILE_CONTEXT( aLog, "embeddedobj (mv76033) OleEmbeddedObject::getPreferredVisualRepresentation" );

    // begin wrapping related part ====================
    uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
    if ( xWrappedObject.is() )
    {
        // the object was converted to OOo embedded object, the current implementation is now only a wrapper
        return xWrappedObject->getPreferredVisualRepresentation( nAspect );
    }
    // end wrapping related part ====================

	::osl::MutexGuard aGuard( m_aMutex );
	if ( m_bDisposed )
		throw lang::DisposedException(); // TODO

	OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!\n" );
	if ( nAspect == embed::Aspects::MSOLE_ICON )
		// no representation can be retrieved
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "Illegal call!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

	// TODO: if the object has cached representation then it should be returned
	// TODO: if the object has no cached representation and is in loaded state it should switch itself to the running state
	if ( m_nObjectState == -1 )
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "The object is not loaded!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

	embed::VisualRepresentation aVisualRepr;

	// TODO: in case of different aspects they must be applied to the mediatype and XTransferable must be used
	// the cache is used only as a fallback if object is not in loaded state
	if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream )
	  && m_nObjectState == embed::EmbedStates::LOADED )
	{
		m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream, sal_True );
		SetVisReplInStream( m_xCachedVisualRepresentation.is() );
	}

	if ( m_xCachedVisualRepresentation.is() )
	{
		return GetVisualRepresentationInNativeFormat_Impl( m_xCachedVisualRepresentation );
	}
#ifdef WNT
	else if ( m_pOleComponent )
	{
		try
		{
			if ( m_nObjectState == embed::EmbedStates::LOADED )
				changeState( embed::EmbedStates::RUNNING );

			datatransfer::DataFlavor aDataFlavor(
                	::rtl::OUString::createFromAscii( "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"" ),
					::rtl::OUString::createFromAscii( "Windows Metafile" ),
					::getCppuType( (const uno::Sequence< sal_Int8 >*) NULL ) );

			aVisualRepr.Data = m_pOleComponent->getTransferData( aDataFlavor );
			aVisualRepr.Flavor = aDataFlavor;

			uno::Sequence< sal_Int8 > aVisReplSeq;
			aVisualRepr.Data >>= aVisReplSeq;
			if ( aVisReplSeq.getLength() )
			{
				m_xCachedVisualRepresentation = GetNewFilledTempStream_Impl( 
						uno::Reference< io::XInputStream > ( static_cast< io::XInputStream* > (
							new ::comphelper::SequenceInputStream( aVisReplSeq ) ) ) );
			}

			return aVisualRepr;
		}
		catch( uno::Exception& )
		{}
	}
#endif

	// the cache is used only as a fallback if object is not in loaded state
	if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) )
	{
		m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream );
		SetVisReplInStream( m_xCachedVisualRepresentation.is() );
	}

	if ( !m_xCachedVisualRepresentation.is() )
	{
		// no representation can be retrieved
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "Illegal call!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );
	}

	return GetVisualRepresentationInNativeFormat_Impl( m_xCachedVisualRepresentation );
}

sal_Int32 SAL_CALL OleEmbeddedObject::getMapUnit( sal_Int64 nAspect )
		throw ( uno::Exception,
				uno::RuntimeException)
{
    // begin wrapping related part ====================
    uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
    if ( xWrappedObject.is() )
    {
        // the object was converted to OOo embedded object, the current implementation is now only a wrapper
        return xWrappedObject->getMapUnit( nAspect );
    }
    // end wrapping related part ====================

	::osl::MutexGuard aGuard( m_aMutex );
	if ( m_bDisposed )
		throw lang::DisposedException(); // TODO

	OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!\n" );
	if ( nAspect == embed::Aspects::MSOLE_ICON )
		// no representation can be retrieved
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "Illegal call!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

	if ( m_nObjectState == -1 )
		throw embed::WrongStateException( ::rtl::OUString::createFromAscii( "The object is not loaded!\n" ),
									uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ) );

	return embed::EmbedMapUnits::ONE_100TH_MM;
}