/**************************************************************
 * 
 * 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_ucb.hxx"

#include <cachedcontentresultsetstub.hxx>
#include <com/sun/star/sdbc/FetchDirection.hpp>
#include <com/sun/star/ucb/FetchError.hpp>
#include <osl/diagnose.h>

using namespace com::sun::star::beans;
using namespace com::sun::star::lang;
using namespace com::sun::star::sdbc;
using namespace com::sun::star::ucb;
using namespace com::sun::star::uno;
using namespace com::sun::star::util;
using namespace cppu;
using namespace rtl;

CachedContentResultSetStub::CachedContentResultSetStub( Reference< XResultSet > xOrigin	)
				: ContentResultSetWrapper( xOrigin )
				, m_nColumnCount( 0 )
				, m_bColumnCountCached( sal_False )
				, m_bNeedToPropagateFetchSize( sal_True )
				, m_bFirstFetchSizePropagationDone( sal_False )
				, m_nLastFetchSize( 1 )//this value is not important at all
				, m_bLastFetchDirection( sal_True )//this value is not important at all
				, m_aPropertyNameForFetchSize( OUString::createFromAscii( "FetchSize" ) )
				, m_aPropertyNameForFetchDirection( OUString::createFromAscii( "FetchDirection" ) )
{
	impl_init();
}

CachedContentResultSetStub::~CachedContentResultSetStub()
{
	impl_deinit();
}

//--------------------------------------------------------------------------
// XInterface methods.
//--------------------------------------------------------------------------
XINTERFACE_COMMON_IMPL( CachedContentResultSetStub )

Any SAL_CALL CachedContentResultSetStub
	::queryInterface( const Type&  rType )
	throw ( RuntimeException )
{
	//list all interfaces inclusive baseclasses of interfaces

	Any aRet = ContentResultSetWrapper::queryInterface( rType );
	if( aRet.hasValue() )
		return aRet;

	aRet = cppu::queryInterface( rType
				, static_cast< XTypeProvider* >( this )
				, static_cast< XServiceInfo* >( this )
				, static_cast< XFetchProvider* >( this )
				, static_cast< XFetchProviderForContentAccess* >( this )
				);

	return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
}

//--------------------------------------------------------------------------
// own methods.  ( inherited )
//--------------------------------------------------------------------------

//virtual
void SAL_CALL CachedContentResultSetStub
	::impl_propertyChange( const PropertyChangeEvent& rEvt )
	throw( RuntimeException )
{
	impl_EnsureNotDisposed();

	//don't notify events on fetchsize and fetchdirection to the above CachedContentResultSet
	//because it will ignore them anyway and we can save this remote calls
	if(	   rEvt.PropertyName == m_aPropertyNameForFetchSize
		|| rEvt.PropertyName == m_aPropertyNameForFetchDirection )
		return;

	PropertyChangeEvent aEvt( rEvt );
	aEvt.Source = static_cast< XPropertySet * >( this );
	aEvt.Further = sal_False;

	impl_notifyPropertyChangeListeners(	aEvt );
}


//virtual
void SAL_CALL CachedContentResultSetStub
	::impl_vetoableChange( const PropertyChangeEvent& rEvt )
	throw( PropertyVetoException,
		   RuntimeException )
{
	impl_EnsureNotDisposed();
	
	//don't notify events on fetchsize and fetchdirection to the above CachedContentResultSet
	//because it will ignore them anyway and we can save this remote calls
	if(	   rEvt.PropertyName == m_aPropertyNameForFetchSize
		|| rEvt.PropertyName == m_aPropertyNameForFetchDirection )
		return;

	PropertyChangeEvent aEvt( rEvt );
	aEvt.Source = static_cast< XPropertySet * >( this );
	aEvt.Further = sal_False;

	impl_notifyVetoableChangeListeners( aEvt );
}

//--------------------------------------------------------------------------
// XTypeProvider methods.
//--------------------------------------------------------------------------

XTYPEPROVIDER_COMMON_IMPL( CachedContentResultSetStub )
//list all interfaces exclusive baseclasses
Sequence< Type > SAL_CALL CachedContentResultSetStub
	::getTypes()
	throw( RuntimeException )
{
	static Sequence< Type >* pTypes = NULL;
	if( !pTypes )
	{
		osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
		if( !pTypes )
		{
			pTypes = new Sequence< Type >(13);
			(*pTypes)[0] = CPPU_TYPE_REF( XTypeProvider );
			(*pTypes)[1] = CPPU_TYPE_REF( XServiceInfo );
			(*pTypes)[2] = CPPU_TYPE_REF( XComponent );
			(*pTypes)[3] = CPPU_TYPE_REF( XCloseable );
			(*pTypes)[4] = CPPU_TYPE_REF( XResultSetMetaDataSupplier );
			(*pTypes)[5] = CPPU_TYPE_REF( XPropertySet );
			(*pTypes)[6] = CPPU_TYPE_REF( XPropertyChangeListener );
			(*pTypes)[7] = CPPU_TYPE_REF( XVetoableChangeListener );
			(*pTypes)[8] = CPPU_TYPE_REF( XResultSet );
			(*pTypes)[9] = CPPU_TYPE_REF( XContentAccess );
			(*pTypes)[10] = CPPU_TYPE_REF( XRow );
			(*pTypes)[11] = CPPU_TYPE_REF( XFetchProvider );
			(*pTypes)[12] = CPPU_TYPE_REF( XFetchProviderForContentAccess );
		}
	}
	return *pTypes;
	/*
	static cppu::OTypeCollection * pCollection = 0;
	if (!pCollection)
	{
		osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
		if (!pCollection)
		{
			static cppu::OTypeCollection
				aTheCollection(
					getCppuType(
						static_cast< Reference< XTypeProvider >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XServiceInfo >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XComponent >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XCloseable >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XResultSetMetaDataSupplier >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XPropertySet >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XPropertyChangeListener >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XVetoableChangeListener >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XResultSet >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XContentAccess >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XRow >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XFetchProvider >
						                 const * >(
							0)),
					getCppuType(
						static_cast< Reference< XFetchProviderForContentAccess >
						                 const * >(
							0))
							);
			pCollection = &aTheCollection;
		}
	}
	return pCollection->getTypes();
	*/
}

//--------------------------------------------------------------------------
// XServiceInfo methods.
//--------------------------------------------------------------------------

XSERVICEINFO_NOFACTORY_IMPL_1( CachedContentResultSetStub,
			 		   OUString::createFromAscii(
						"com.sun.star.comp.ucb.CachedContentResultSetStub" ),
			 		   OUString::createFromAscii(
						CACHED_CRS_STUB_SERVICE_NAME ) );

//-----------------------------------------------------------------
// XFetchProvider methods.
//-----------------------------------------------------------------

#define FETCH_XXX( impl_loadRow, loadInterface ) \
impl_EnsureNotDisposed(); \
if( !m_xResultSetOrigin.is() ) \
{ \
	OSL_ENSURE( sal_False, "broadcaster was disposed already" ); \
	throw RuntimeException(); \
} \
impl_propagateFetchSizeAndDirection( nRowCount, bDirection ); \
FetchResult aRet; \
aRet.StartIndex = nRowStartPosition; \
aRet.Orientation = bDirection; \
aRet.FetchError = FetchError::SUCCESS; /*ENDOFDATA, EXCEPTION*/ \
sal_Int32 nOldOriginal_Pos = m_xResultSetOrigin->getRow(); \
if( impl_isForwardOnly() ) \
{ \
	if( nOldOriginal_Pos != nRowStartPosition ) \
	{ \
		/*@todo*/ \
		aRet.FetchError = FetchError::EXCEPTION; \
		return aRet; \
	} \
	if( nRowCount != 1 ) \
		aRet.FetchError = FetchError::EXCEPTION; \
 \
	aRet.Rows.realloc( 1 ); \
 \
	try \
	{ \
		impl_loadRow( aRet.Rows[0], loadInterface ); \
	} \
	catch( SQLException& ) \
	{ \
		aRet.Rows.realloc( 0 ); \
		aRet.FetchError = FetchError::EXCEPTION; \
		return aRet; \
	} \
	return aRet; \
} \
aRet.Rows.realloc( nRowCount ); \
sal_Bool bOldOriginal_AfterLast = sal_False; \
if( !nOldOriginal_Pos ) \
	bOldOriginal_AfterLast = m_xResultSetOrigin->isAfterLast(); \
sal_Int32 nN = 1; \
sal_Bool bValidNewPos = sal_False; \
try \
{ \
	try \
	{ \
		/*if( nOldOriginal_Pos != nRowStartPosition )*/ \
		bValidNewPos = m_xResultSetOrigin->absolute( nRowStartPosition ); \
	} \
	catch( SQLException& ) \
	{ \
		aRet.Rows.realloc( 0 ); \
		aRet.FetchError = FetchError::EXCEPTION; \
		return aRet; \
	} \
	if( !bValidNewPos ) \
	{ \
		aRet.Rows.realloc( 0 ); \
		aRet.FetchError = FetchError::EXCEPTION; \
 \
		/*restore old position*/ \
		if( nOldOriginal_Pos ) \
			m_xResultSetOrigin->absolute( nOldOriginal_Pos ); \
		else if( bOldOriginal_AfterLast ) \
			m_xResultSetOrigin->afterLast(); \
		else \
			m_xResultSetOrigin->beforeFirst(); \
 \
		return aRet; \
	} \
	for( ; nN <= nRowCount; ) \
	{ \
		impl_loadRow( aRet.Rows[nN-1], loadInterface ); \
		nN++; \
		if( nN <= nRowCount ) \
		{ \
			if( bDirection ) \
			{ \
				if( !m_xResultSetOrigin->next() ) \
				{ \
					aRet.Rows.realloc( nN-1 ); \
					aRet.FetchError = FetchError::ENDOFDATA; \
					break; \
				} \
			} \
			else \
			{ \
				if( !m_xResultSetOrigin->previous() ) \
				{ \
					aRet.Rows.realloc( nN-1 ); \
					aRet.FetchError = FetchError::ENDOFDATA; \
					break; \
				} \
			} \
		} \
	} \
} \
catch( SQLException& ) \
{ \
	aRet.Rows.realloc( nN-1 ); \
	aRet.FetchError = FetchError::EXCEPTION; \
} \
/*restore old position*/ \
if( nOldOriginal_Pos ) \
	m_xResultSetOrigin->absolute( nOldOriginal_Pos ); \
else if( bOldOriginal_AfterLast ) \
	m_xResultSetOrigin->afterLast(); \
else \
	m_xResultSetOrigin->beforeFirst(); \
return aRet;

FetchResult SAL_CALL CachedContentResultSetStub
	::fetch( sal_Int32 nRowStartPosition
	, sal_Int32 nRowCount, sal_Bool bDirection )
	throw( RuntimeException )
{
	impl_init_xRowOrigin();
	FETCH_XXX( impl_getCurrentRowContent, m_xRowOrigin );
}

sal_Int32 SAL_CALL CachedContentResultSetStub
	::impl_getColumnCount()
{
	sal_Int32 nCount;
	sal_Bool bCached;
	{
		osl::Guard< osl::Mutex > aGuard( m_aMutex );
		nCount = m_nColumnCount;
		bCached = m_bColumnCountCached;
	}
	if( !bCached )
	{
		try
		{
			Reference< XResultSetMetaData > xMetaData = getMetaData();
			if( xMetaData.is() )
				nCount = xMetaData->getColumnCount();
		}
		catch( SQLException& )
		{
			OSL_ENSURE( sal_False, "couldn't determine the column count" );
			nCount = 0;
		}
	}
	osl::Guard< osl::Mutex > aGuard( m_aMutex );
	m_nColumnCount = nCount;
	m_bColumnCountCached = sal_True;
	return m_nColumnCount;
}

void SAL_CALL CachedContentResultSetStub
	::impl_getCurrentRowContent( Any& rRowContent
		, Reference< XRow > xRow )
		throw ( SQLException, RuntimeException )
{
	sal_Int32 nCount = impl_getColumnCount();

	Sequence< Any > aContent( nCount );
	for( sal_Int32 nN = 1; nN <= nCount; nN++ )
	{
		aContent[nN-1] = xRow->getObject( nN, NULL );
	}

	rRowContent <<= aContent;
}

void SAL_CALL CachedContentResultSetStub
	::impl_propagateFetchSizeAndDirection( sal_Int32 nFetchSize, sal_Bool bFetchDirection )
		throw ( RuntimeException )
{
	//this is done only for the case, that there is another CachedContentResultSet in the chain of underlying ResulSets

	//we do not propagate the property 'FetchSize' or 'FetchDirection' via 'setPropertyValue' from the above CachedContentResultSet to save remote calls

	//if the underlying ResultSet has a property FetchSize and FetchDirection,
	//we will set these properties, if the new given parameters are different from the last ones

	if( !m_bNeedToPropagateFetchSize )
		return;

	sal_Bool bNeedAction;
	sal_Int32 nLastSize;
	sal_Bool bLastDirection;
	sal_Bool bFirstPropagationDone;
	{
		osl::Guard< osl::Mutex > aGuard( m_aMutex );
		bNeedAction				= m_bNeedToPropagateFetchSize;
		nLastSize				= m_nLastFetchSize;
		bLastDirection			= m_bLastFetchDirection;
		bFirstPropagationDone	= m_bFirstFetchSizePropagationDone;
	}
	if( bNeedAction )
	{
		if( nLastSize == nFetchSize
			&& bLastDirection == bFetchDirection
			&& bFirstPropagationDone == sal_True )
			return;

		if(!bFirstPropagationDone)
		{
			//check wether the properties 'FetchSize' and 'FetchDirection' do exist
			
			Reference< XPropertySetInfo > xPropertySetInfo = getPropertySetInfo();
			sal_Bool bHasSize = xPropertySetInfo->hasPropertyByName( m_aPropertyNameForFetchSize ); 
			sal_Bool bHasDirection = xPropertySetInfo->hasPropertyByName( m_aPropertyNameForFetchDirection ); 

			if(!bHasSize || !bHasDirection)
			{
				osl::Guard< osl::Mutex > aGuard( m_aMutex );
				m_bNeedToPropagateFetchSize = sal_False;
				return;
			}
		}
		
		sal_Bool bSetSize		= ( nLastSize		!=nFetchSize		) || !bFirstPropagationDone;
		sal_Bool bSetDirection	= ( bLastDirection	!=bFetchDirection	) || !bFirstPropagationDone;

		{
			osl::Guard< osl::Mutex > aGuard( m_aMutex );
			m_bFirstFetchSizePropagationDone = sal_True;
			m_nLastFetchSize		= nFetchSize;
			m_bLastFetchDirection	= bFetchDirection;
		}
		
		if( bSetSize )
		{
			Any aValue;
			aValue <<= nFetchSize;
			try
			{
				setPropertyValue( m_aPropertyNameForFetchSize, aValue );
			}
			catch( com::sun::star::uno::Exception& ) {}
		}
		if( bSetDirection )
		{
			sal_Int32 nFetchDirection = FetchDirection::FORWARD;
			if( !bFetchDirection )
				nFetchDirection = FetchDirection::REVERSE;
			Any aValue;
			aValue <<= nFetchDirection;
			try
			{
				setPropertyValue( m_aPropertyNameForFetchDirection, aValue );
			}
			catch( com::sun::star::uno::Exception& ) {}
		}

	}
}

//-----------------------------------------------------------------
// XFetchProviderForContentAccess methods.
//-----------------------------------------------------------------

void SAL_CALL CachedContentResultSetStub
	::impl_getCurrentContentIdentifierString( Any& rAny
		, Reference< XContentAccess > xContentAccess )
		throw ( RuntimeException )
{
 	rAny <<= xContentAccess->queryContentIdentifierString();
}

void SAL_CALL CachedContentResultSetStub
	::impl_getCurrentContentIdentifier( Any& rAny
		, Reference< XContentAccess > xContentAccess )
		throw ( RuntimeException )
{
 	rAny <<= xContentAccess->queryContentIdentifier();
}

void SAL_CALL CachedContentResultSetStub
	::impl_getCurrentContent( Any& rAny
		, Reference< XContentAccess > xContentAccess )
		throw ( RuntimeException )
{
 	rAny <<= xContentAccess->queryContent();
}

//virtual
FetchResult SAL_CALL CachedContentResultSetStub
	::fetchContentIdentifierStrings( sal_Int32 nRowStartPosition
		, sal_Int32 nRowCount, sal_Bool bDirection )
		throw( com::sun::star::uno::RuntimeException )
{
	impl_init_xContentAccessOrigin();
	FETCH_XXX( impl_getCurrentContentIdentifierString, m_xContentAccessOrigin );
}

//virtual
FetchResult SAL_CALL CachedContentResultSetStub
	::fetchContentIdentifiers( sal_Int32 nRowStartPosition
		, sal_Int32 nRowCount, sal_Bool bDirection )
		throw( com::sun::star::uno::RuntimeException )
{
	impl_init_xContentAccessOrigin();
	FETCH_XXX( impl_getCurrentContentIdentifier, m_xContentAccessOrigin );
}

//virtual
FetchResult SAL_CALL CachedContentResultSetStub
	::fetchContents( sal_Int32 nRowStartPosition
		, sal_Int32 nRowCount, sal_Bool bDirection )
		throw( com::sun::star::uno::RuntimeException )
{
	impl_init_xContentAccessOrigin();
	FETCH_XXX( impl_getCurrentContent, m_xContentAccessOrigin );
}

//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// class CachedContentResultSetStubFactory
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

CachedContentResultSetStubFactory::CachedContentResultSetStubFactory(
		const Reference< XMultiServiceFactory > & rSMgr )
{
	m_xSMgr = rSMgr;
}

CachedContentResultSetStubFactory::~CachedContentResultSetStubFactory()
{
}

//--------------------------------------------------------------------------
// CachedContentResultSetStubFactory XInterface methods.
//--------------------------------------------------------------------------

XINTERFACE_IMPL_3( CachedContentResultSetStubFactory,
				   XTypeProvider,
				   XServiceInfo,
				   XCachedContentResultSetStubFactory );

//--------------------------------------------------------------------------
// CachedContentResultSetStubFactory XTypeProvider methods.
//--------------------------------------------------------------------------

XTYPEPROVIDER_IMPL_3( CachedContentResultSetStubFactory,
					  XTypeProvider,
				   	  XServiceInfo,
					  XCachedContentResultSetStubFactory );

//--------------------------------------------------------------------------
// CachedContentResultSetStubFactory XServiceInfo methods.
//--------------------------------------------------------------------------

XSERVICEINFO_IMPL_1( CachedContentResultSetStubFactory,
 		   		 OUString::createFromAscii(
				 	"com.sun.star.comp.ucb.CachedContentResultSetStubFactory" ),
 		   		 OUString::createFromAscii(
				 	CACHED_CRS_STUB_FACTORY_NAME ) );

//--------------------------------------------------------------------------
// Service factory implementation.
//--------------------------------------------------------------------------

ONE_INSTANCE_SERVICE_FACTORY_IMPL( CachedContentResultSetStubFactory );

//--------------------------------------------------------------------------
// CachedContentResultSetStubFactory XCachedContentResultSetStubFactory methods.
//--------------------------------------------------------------------------

	//virtual
Reference< XResultSet > SAL_CALL CachedContentResultSetStubFactory
	::createCachedContentResultSetStub(
			const Reference< XResultSet > & xSource )
			throw( RuntimeException )
{
	if( xSource.is() )
	{
		Reference< XResultSet > xRet;
		xRet = new CachedContentResultSetStub( xSource );
		return xRet;
	}
	return NULL;
}


