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

#include <sfx2/linksrc.hxx>
#include <sfx2/lnkbase.hxx>
//#include <sot/exchange.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>

#include <tools/debug.hxx>
#include <vcl/timer.hxx>
#include <svl/svarray.hxx>


using namespace ::com::sun::star::uno;

namespace sfx2
{

TYPEINIT0( SvLinkSource )

/************** class SvLinkSourceTimer *********************************/
class SvLinkSourceTimer : public Timer
{
	SvLinkSource *  pOwner;
	virtual void    Timeout();
public:
			SvLinkSourceTimer( SvLinkSource * pOwn );
};

SvLinkSourceTimer::SvLinkSourceTimer( SvLinkSource * pOwn )
	: pOwner( pOwn )
{
}

void SvLinkSourceTimer::Timeout()
{
	// sicher gegen zerstoeren im Handler
	SvLinkSourceRef aAdv( pOwner );
	pOwner->SendDataChanged();
}

static void StartTimer( SvLinkSourceTimer ** ppTimer, SvLinkSource * pOwner,
						sal_uIntPtr nTimeout )
{
	if( !*ppTimer )
	{
		*ppTimer = new SvLinkSourceTimer( pOwner );
		(*ppTimer)->SetTimeout( nTimeout );
		(*ppTimer)->Start();
	}
}


struct SvLinkSource_Entry_Impl
{
	SvBaseLinkRef		xSink;
	String				aDataMimeType;
	sal_uInt16				nAdviseModes;
	sal_Bool 				bIsDataSink;

	SvLinkSource_Entry_Impl( SvBaseLink* pLink, const String& rMimeType,
								sal_uInt16 nAdvMode )
		: xSink( pLink ), aDataMimeType( rMimeType ),
			nAdviseModes( nAdvMode ), bIsDataSink( sal_True )
	{}

	SvLinkSource_Entry_Impl( SvBaseLink* pLink )
		: xSink( pLink ), nAdviseModes( 0 ), bIsDataSink( sal_False )
	{}

	~SvLinkSource_Entry_Impl();
};

SvLinkSource_Entry_Impl::~SvLinkSource_Entry_Impl()
{
}

typedef SvLinkSource_Entry_Impl* SvLinkSource_Entry_ImplPtr;
SV_DECL_PTRARR_DEL( SvLinkSource_Array_Impl, SvLinkSource_Entry_ImplPtr, 4, 4 )
SV_IMPL_PTRARR( SvLinkSource_Array_Impl, SvLinkSource_Entry_ImplPtr );

class SvLinkSource_EntryIter_Impl
{
	SvLinkSource_Array_Impl aArr;
	const SvLinkSource_Array_Impl& rOrigArr;
	sal_uInt16 nPos;
public:
	SvLinkSource_EntryIter_Impl( const SvLinkSource_Array_Impl& rArr );
	~SvLinkSource_EntryIter_Impl();
	SvLinkSource_Entry_Impl* Curr()
							{ return nPos < aArr.Count() ? aArr[ nPos ] : 0; }
	SvLinkSource_Entry_Impl* Next();
	sal_Bool IsValidCurrValue( SvLinkSource_Entry_Impl* pEntry );
};

SvLinkSource_EntryIter_Impl::SvLinkSource_EntryIter_Impl(
		const SvLinkSource_Array_Impl& rArr )
	: rOrigArr( rArr ), nPos( 0 )
{
	aArr.Insert( &rArr, 0 );
}
SvLinkSource_EntryIter_Impl::~SvLinkSource_EntryIter_Impl()
{
	aArr.Remove( 0, aArr.Count() );
}

sal_Bool SvLinkSource_EntryIter_Impl::IsValidCurrValue( SvLinkSource_Entry_Impl* pEntry )
{
	return ( nPos < aArr.Count() && aArr[nPos] == pEntry && USHRT_MAX != rOrigArr.GetPos( pEntry ) );
}

SvLinkSource_Entry_Impl* SvLinkSource_EntryIter_Impl::Next()
{
	SvLinkSource_Entry_ImplPtr pRet = 0;
	if( nPos + 1 < aArr.Count() )
	{
		++nPos;
		if( rOrigArr.Count() == aArr.Count() &&
			rOrigArr[ nPos ] == aArr[ nPos ] )
			pRet = aArr[ nPos ];
		else
		{
			// then we must search the current (or the next) in the orig
			do {
				pRet = aArr[ nPos ];
				if( USHRT_MAX != rOrigArr.GetPos( pRet ))
					break;
				pRet = 0;
				++nPos;
			} while( nPos < aArr.Count() );

			if( nPos >= aArr.Count() )
				pRet = 0;
		}
	}
	return pRet;
}

struct SvLinkSource_Impl
{
	SvLinkSource_Array_Impl aArr;
	String				aDataMimeType;
	SvLinkSourceTimer *	pTimer;
	sal_uIntPtr				nTimeout;
    com::sun::star::uno::Reference<com::sun::star::io::XInputStream>
    m_xInputStreamToLoadFrom;
    sal_Bool m_bIsReadOnly;

	SvLinkSource_Impl() : pTimer( 0 ), nTimeout( 3000 ) {}
	~SvLinkSource_Impl();

	void Closed();
};

SvLinkSource_Impl::~SvLinkSource_Impl()
{
	delete pTimer;
}

SvLinkSource::SvLinkSource()
 	: pImpl( new SvLinkSource_Impl )
{
}

SvLinkSource::~SvLinkSource()
{
	delete pImpl;
}


SvLinkSource::StreamToLoadFrom SvLinkSource::getStreamToLoadFrom()
{
    return StreamToLoadFrom(
        pImpl->m_xInputStreamToLoadFrom,
        pImpl->m_bIsReadOnly);
}

void SvLinkSource::setStreamToLoadFrom(const com::sun::star::uno::Reference<com::sun::star::io::XInputStream>& xInputStream,sal_Bool bIsReadOnly )
{
    pImpl->m_xInputStreamToLoadFrom = xInputStream;
    pImpl->m_bIsReadOnly = bIsReadOnly;
}

// --> OD 2008-06-18 #i88291#
void SvLinkSource::clearStreamToLoadFrom()
{
    pImpl->m_xInputStreamToLoadFrom.clear();
}
// <--

void  SvLinkSource::Closed()
{
	SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
	for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() )
		if( !p->bIsDataSink )
			p->xSink->Closed();
}

sal_uIntPtr SvLinkSource::GetUpdateTimeout() const
{
	return pImpl->nTimeout;
}

void SvLinkSource::SetUpdateTimeout( sal_uIntPtr nTimeout )
{
	pImpl->nTimeout = nTimeout;
	if( pImpl->pTimer )
		pImpl->pTimer->SetTimeout( nTimeout );
}

void SvLinkSource::SendDataChanged()
{
	SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
	for( SvLinkSource_Entry_ImplPtr p = aIter.Curr(); p; p = aIter.Next() )
	{
		if( p->bIsDataSink )
		{
			String sDataMimeType( pImpl->aDataMimeType );
			if( !sDataMimeType.Len() )
				sDataMimeType = p->aDataMimeType;

			Any aVal;
			if( ( p->nAdviseModes & ADVISEMODE_NODATA ) ||
				GetData( aVal, sDataMimeType, sal_True ) )
			{
				p->xSink->DataChanged( sDataMimeType, aVal );

				if ( !aIter.IsValidCurrValue( p ) )
					continue;

				if( p->nAdviseModes & ADVISEMODE_ONLYONCE )
				{
					sal_uInt16 nFndPos = pImpl->aArr.GetPos( p );
					if( USHRT_MAX != nFndPos )
						pImpl->aArr.DeleteAndDestroy( nFndPos );
				}

			}
		}
	}
	if( pImpl->pTimer )
	{
		delete pImpl->pTimer;
		pImpl->pTimer = NULL;
	}
	pImpl->aDataMimeType.Erase();
}

void SvLinkSource::NotifyDataChanged()
{
	if( pImpl->nTimeout )
		StartTimer( &pImpl->pTimer, this, pImpl->nTimeout ); // Timeout neu
	else
	{
		SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
		for( SvLinkSource_Entry_ImplPtr p = aIter.Curr(); p; p = aIter.Next() )
			if( p->bIsDataSink )
			{
				Any aVal;
				if( ( p->nAdviseModes & ADVISEMODE_NODATA ) ||
					GetData( aVal, p->aDataMimeType, sal_True ) )
				{
					p->xSink->DataChanged( p->aDataMimeType, aVal );

					if ( !aIter.IsValidCurrValue( p ) )
						continue;

					if( p->nAdviseModes & ADVISEMODE_ONLYONCE )
					{
						sal_uInt16 nFndPos = pImpl->aArr.GetPos( p );
						if( USHRT_MAX != nFndPos )
							pImpl->aArr.DeleteAndDestroy( nFndPos );
					}
				}
			}

			if( pImpl->pTimer )
			{
				delete pImpl->pTimer;
				pImpl->pTimer = NULL;
			}
	}
}

// notify the sink, the mime type is not
// a selection criterion
void SvLinkSource::DataChanged( const String & rMimeType,
								const ::com::sun::star::uno::Any & rVal )
{
	if( pImpl->nTimeout && !rVal.hasValue() )
	{   // nur wenn keine Daten mitgegeben wurden
		// fire all data to the sink, independent of the requested format
		pImpl->aDataMimeType = rMimeType;
		StartTimer( &pImpl->pTimer, this, pImpl->nTimeout ); // Timeout neu
	}
	else
	{
		SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
		for( SvLinkSource_Entry_ImplPtr p = aIter.Curr(); p; p = aIter.Next() )
		{
			if( p->bIsDataSink )
			{
				p->xSink->DataChanged( rMimeType, rVal );

				if ( !aIter.IsValidCurrValue( p ) )
					continue;

				if( p->nAdviseModes & ADVISEMODE_ONLYONCE )
				{
					sal_uInt16 nFndPos = pImpl->aArr.GetPos( p );
					if( USHRT_MAX != nFndPos )
						pImpl->aArr.DeleteAndDestroy( nFndPos );
				}
			}
		}

		if( pImpl->pTimer )
		{
			delete pImpl->pTimer;
			pImpl->pTimer = NULL;
		}
	}
}


// only one link is correct
void SvLinkSource::AddDataAdvise( SvBaseLink * pLink, const String& rMimeType,
									sal_uInt16 nAdviseModes )
{
	SvLinkSource_Entry_ImplPtr pNew = new SvLinkSource_Entry_Impl(
					pLink, rMimeType, nAdviseModes );
	pImpl->aArr.Insert( pNew, pImpl->aArr.Count() );
}

void SvLinkSource::RemoveAllDataAdvise( SvBaseLink * pLink )
{
	SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
	for( SvLinkSource_Entry_ImplPtr p = aIter.Curr(); p; p = aIter.Next() )
		if( p->bIsDataSink && &p->xSink == pLink )
		{
			sal_uInt16 nFndPos = pImpl->aArr.GetPos( p );
			if( USHRT_MAX != nFndPos )
				pImpl->aArr.DeleteAndDestroy( nFndPos );
		}
}

// only one link is correct
void SvLinkSource::AddConnectAdvise( SvBaseLink * pLink )
{
	SvLinkSource_Entry_ImplPtr pNew = new SvLinkSource_Entry_Impl( pLink );
	pImpl->aArr.Insert( pNew, pImpl->aArr.Count() );
}

void SvLinkSource::RemoveConnectAdvise( SvBaseLink * pLink )
{
	SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
	for( SvLinkSource_Entry_ImplPtr p = aIter.Curr(); p; p = aIter.Next() )
		if( !p->bIsDataSink && &p->xSink == pLink )
		{
			sal_uInt16 nFndPos = pImpl->aArr.GetPos( p );
			if( USHRT_MAX != nFndPos )
				pImpl->aArr.DeleteAndDestroy( nFndPos );
		}
}

sal_Bool SvLinkSource::HasDataLinks( const SvBaseLink* pLink ) const
{
	sal_Bool bRet = sal_False;
	const SvLinkSource_Entry_Impl* p;
	for( sal_uInt16 n = 0, nEnd = pImpl->aArr.Count(); n < nEnd; ++n )
		if( ( p = pImpl->aArr[ n ] )->bIsDataSink &&
			( !pLink || &p->xSink == pLink ) )
		{
			bRet = sal_True;
			break;
		}
	return bRet;
}

// sal_True => waitinmg for data
sal_Bool SvLinkSource::IsPending() const
{
	return sal_False;
}

// sal_True => data complete loaded
sal_Bool SvLinkSource::IsDataComplete() const
{
	return sal_True;
}

sal_Bool SvLinkSource::Connect( SvBaseLink* )
{
	return sal_True;
}

sal_Bool SvLinkSource::GetData( ::com::sun::star::uno::Any &, const String &, sal_Bool )
{
	return sal_False;
}

void SvLinkSource::Edit( Window *, SvBaseLink *, const Link& )
{
}

}

