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

#define UNICODE
#include "ddeimp.hxx"
#include <svl/svdde.hxx>
#include <svl/svarray.hxx>
#include <tools/debug.hxx>
#include <osl/thread.h>

//static long         hCurConv  = 0;
//static DWORD        hDdeInst  = NULL;
//static short        nInstance = 0;
//static DdeServices* pServices;

enum DdeItemType
{
	DDEITEM,
	DDEGETPUTITEM
};

struct DdeItemImpData
{
	sal_uLong nHCnv;
	sal_uInt16 nCnt;

	DdeItemImpData( sal_uLong nH ) : nHCnv( nH ), nCnt( 1 ) {}
};

SV_DECL_VARARR( DdeItemImp, DdeItemImpData, 1, 1 )
SV_IMPL_VARARR( DdeItemImp, DdeItemImpData )

// --- DdeInternat::SvrCallback() ----------------------------------

#ifdef WNT
HDDEDATA CALLBACK DdeInternal::SvrCallback(
			WORD nCode, WORD nCbType, HCONV hConv, HSZ hText1, HSZ hText2,
			HDDEDATA hData, DWORD, DWORD )
#else
#if defined ( MTW ) || ( defined ( GCC ) && defined ( OS2 )) || defined( ICC )
HDDEDATA CALLBACK __EXPORT DdeInternal::SvrCallback(
			WORD nCode, WORD nCbType, HCONV hConv, HSZ hText1, HSZ hText2,
			HDDEDATA hData, DWORD, DWORD )
#else
HDDEDATA CALLBACK _export DdeInternal::SvrCallback(
			WORD nCode, WORD nCbType, HCONV hConv, HSZ hText1, HSZ hText2,
			HDDEDATA hData, DWORD, DWORD )
#endif
#endif
{
	DdeServices&    rAll = DdeService::GetServices();
	DdeService*     pService;
	DdeTopic*       pTopic;
	DdeItem*        pItem;
	DdeData*        pData;
	Conversation*   pC;

	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");

	switch( nCode )
	{
		case XTYP_WILDCONNECT:
		{
			int nTopics = 0;

#if 1
			TCHAR chTopicBuf[250];
			if( hText1 )
				DdeQueryString( pInst->hDdeInstSvr, hText1, chTopicBuf,
								sizeof(chTopicBuf)/sizeof(TCHAR), CP_WINUNICODE );

			for( pService = rAll.First();pService;pService = rAll.Next() )
			{
				if ( !hText2 || ( *pService->pName == hText2 ) )
				{
					String sTopics( pService->Topics() );
					if( sTopics.Len() )
					{
						if( hText1 )
						{
							sal_uInt16 n = 0;
							while( STRING_NOTFOUND != n )
							{
								String s( sTopics.GetToken( 0, '\t', n ));
								if( s == reinterpret_cast<const sal_Unicode*>(chTopicBuf) )
									++nTopics;
							}
						}
						else
							nTopics += sTopics.GetTokenCount( '\t' );
					}
				}
			}

#else
			for( pService = rAll.First();pService;pService = rAll.Next() )
			{
				if ( !hText2 || ( *pService->pName == hText2 ) )
				{
					for( pTopic = pService->aTopics.First(); pTopic;
						 pTopic = pService->aTopics.Next() )
					{
						if ( !hText1 || (*pTopic->pName == hText1) )
							nTopics++;
					}
				}
			}
#endif
			if( !nTopics )
				return (HDDEDATA)NULL;

			HSZPAIR* pPairs = new HSZPAIR [nTopics + 1];
			if ( !pPairs )
				return (HDDEDATA)NULL;

			HSZPAIR* q = pPairs;
			for( pService = rAll.First(); pService; pService = rAll.Next() )
			{
				if ( !hText2 || (*pService->pName == hText2 ) )
				{
#if 0
					for ( pTopic = pService->aTopics.First(); pTopic;
						  pTopic = pService->aTopics.Next() )
					{
						if ( !hText1 || (*pTopic->pName == hText1) )
						{
							q->hszSvc   = *pService->pName;
							q->hszTopic = *pTopic->pName;
							q++;
						}
					}
#else
					String sTopics( pService->Topics() );
					sal_uInt16 n = 0;
					while( STRING_NOTFOUND != n )
					{
						String s( sTopics.GetToken( 0, '\t', n ));
						s.EraseAllChars( '\n' ).EraseAllChars( '\r' );
						if( !hText1 || s == reinterpret_cast<const sal_Unicode*>(chTopicBuf) )
						{
							DdeString aDStr( pInst->hDdeInstSvr, s );
							pTopic = FindTopic( *pService, (HSZ)aDStr );
							if( pTopic )
							{
								q->hszSvc   = *pService->pName;
								q->hszTopic = *pTopic->pName;
								q++;
							}
						}
					}

#endif
				}
			}

			q->hszSvc   = NULL;
			q->hszTopic = NULL;
			HDDEDATA h = DdeCreateDataHandle(
							pInst->hDdeInstSvr, (LPBYTE) pPairs,
							sizeof(HSZPAIR) * (nTopics+1),
							0, NULL, nCbType, 0);
			delete [] pPairs;
			return h;
		}

		case XTYP_CONNECT:
			pService = FindService( hText2 );
			if ( pService)
				pTopic = FindTopic( *pService, hText1 );
			else
				pTopic = NULL;
			if ( pTopic )
				return (HDDEDATA)DDE_FACK;
			else
				return (HDDEDATA) NULL;

		case XTYP_CONNECT_CONFIRM:
			pService = FindService( hText2 );
			if ( pService )
			{
				pTopic = FindTopic( *pService, hText1 );
				if ( pTopic )
				{
					pTopic->Connect( (long) hConv );
					pC = new Conversation;
					pC->hConv = hConv;
					pC->pTopic = pTopic;
					pService->pConv->Insert( pC );
				}
			}
			return (HDDEDATA)NULL;
	}

	for ( pService = rAll.First(); pService; pService = rAll.Next() )
	{
		for( pC = pService->pConv->First(); pC;
			 pC = pService->pConv->Next() )
		{
			if ( pC->hConv == hConv )
				goto found;
		}
	}

	return (HDDEDATA) DDE_FNOTPROCESSED;

found:
	if ( nCode == XTYP_DISCONNECT)
	{
		pC->pTopic->_Disconnect( (long) hConv );
		pService->pConv->Remove( pC );
		delete pC;
		return (HDDEDATA)NULL;
	}

	sal_Bool bExec = sal_Bool(nCode == XTYP_EXECUTE);
	pTopic = pC->pTopic;
	if ( pTopic && !bExec )
		pItem = FindItem( *pTopic, hText2 );
	else
		pItem = NULL;

	if ( !bExec && !pService->HasCbFormat( nCbType ) )
		pItem = NULL;
	if ( !pItem && !bExec )
		return (HDDEDATA)DDE_FNOTPROCESSED;
	if ( pItem )
		pTopic->aItem = pItem->GetName();
	else
		pTopic->aItem.Erase();

	sal_Bool bRes = sal_False;
	pInst->hCurConvSvr = (long)hConv;
	switch( nCode )
	{
		case XTYP_REQUEST:
		case XTYP_ADVREQ:
			{
			String aRes;          // darf erst am Ende freigegeben werden!!
			if ( pTopic->IsSystemTopic() )
			{
				if ( pTopic->aItem == reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_TOPICS) )
					aRes = pService->Topics();
				else if ( pTopic->aItem == reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_SYSITEMS) )
					aRes = pService->SysItems();
				else if ( pTopic->aItem == reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_STATUS) )
					aRes = pService->Status();
				else if ( pTopic->aItem == reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_FORMATS) )
					aRes = pService->Formats();
				else if ( pTopic->aItem ==  reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_HELP) )
					aRes = pService->GetHelp();
				else
					aRes = pService->SysTopicGet( pTopic->aItem );

				if ( aRes.Len() )
					pData = new DdeData( aRes );
				else
					pData = NULL;
			}
			else if( DDEGETPUTITEM == pItem->nType )
				pData = ((DdeGetPutItem*)pItem)->Get(
							DdeData::GetInternalFormat( nCbType ) );
			else
				pData = pTopic->Get( DdeData::GetInternalFormat( nCbType ));

			if ( pData )
				return DdeCreateDataHandle( pInst->hDdeInstSvr,
											(LPBYTE)pData->pImp->pData,
											pData->pImp->nData,
											0, hText2,
											DdeData::GetExternalFormat(
												pData->pImp->nFmt ),
											0 );
			}
			break;

		case XTYP_POKE:
			if ( !pTopic->IsSystemTopic() )
			{
				DdeData d;
				d.pImp->hData = hData;
				d.pImp->nFmt  = DdeData::GetInternalFormat( nCbType );
				d.Lock();
				if( DDEGETPUTITEM == pItem->nType )
					bRes = ((DdeGetPutItem*)pItem)->Put( &d );
				else
					bRes = pTopic->Put( &d );
			}
			pInst->hCurConvSvr = NULL;
			if ( bRes )
				return (HDDEDATA)DDE_FACK;
			else
				return (HDDEDATA) DDE_FNOTPROCESSED;

		case XTYP_ADVSTART:
			{
				// wird das Item zum erstenmal ein HotLink ?
				if( !pItem->pImpData && pTopic->StartAdviseLoop() )
				{
					// dann wurde das Item ausgewechselt
					pTopic->aItems.Remove( pItem );
					DdeItem* pTmp;
					for(  pTmp = pTopic->aItems.First(); pTmp;
									pTmp = pTopic->aItems.Next() )
						if( *pTmp->pName == hText2 )
						{
							// es wurde tatsaechlich ausgewechselt
							delete pItem;
							pItem = 0;
							break;
						}
					if( pItem )
						// es wurde doch nicht ausgewechselt, also wieder rein
						pTopic->aItems.Insert( pItem );
					else
						pItem = pTmp;
				}
				pItem->IncMonitor( (long)hConv );
				pInst->hCurConvSvr = NULL;
			}
			return (HDDEDATA)sal_True;

		case XTYP_ADVSTOP:
			pItem->DecMonitor( (long)hConv );
			if( !pItem->pImpData )
				pTopic->StopAdviseLoop();
			pInst->hCurConvSvr = NULL;
			return (HDDEDATA)sal_True;

		case XTYP_EXECUTE:
			{
				DdeData aExec;
				aExec.pImp->hData = hData;
				aExec.pImp->nFmt  = DdeData::GetInternalFormat( nCbType );
				aExec.Lock();
				String aName;

				aName = (const sal_Unicode *)aExec.pImp->pData;

				if( pTopic->IsSystemTopic() )
					bRes = pService->SysTopicExecute( &aName );
				else
					bRes = pTopic->Execute( &aName );
			}
			pInst->hCurConvSvr = NULL;
			if ( bRes )
				return (HDDEDATA)DDE_FACK;
			else
				return (HDDEDATA)DDE_FNOTPROCESSED;
	}

	return (HDDEDATA)NULL;
}

// --- DdeInternat::FindService() ----------------------------------

DdeService* DdeInternal::FindService( HSZ hService )
{
	DdeService*  s;
	DdeServices& rSvc = DdeService::GetServices();
	for ( s = rSvc.First(); s; s = rSvc.Next() )
	{
		if ( *s->pName == hService )
			return s;
	}

	return NULL;
}

// --- DdeInternat::FindTopic() ------------------------------------

DdeTopic* DdeInternal::FindTopic( DdeService& rService, HSZ hTopic )
{
	DdeTopic* s;
	DdeTopics& rTopics = rService.aTopics;
	int bWeiter = sal_False;
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");

	do {            // middle check loop
		for ( s = rTopics.First(); s; s = rTopics.Next() )
		{
			if ( *s->pName == hTopic )
				return s;
		}

		bWeiter = !bWeiter;
		if( !bWeiter )
			break;

		// dann befragen wir doch mal unsere Ableitung:
		TCHAR chBuf[250];
		DdeQueryString(pInst->hDdeInstSvr,hTopic,chBuf,sizeof(chBuf)/sizeof(TCHAR),CP_WINUNICODE );
		bWeiter = rService.MakeTopic( reinterpret_cast<const sal_Unicode*>(chBuf) );
		// dann muessen wir noch mal suchen
	} while( bWeiter );

	return 0;
}

// --- DdeInternal::FindItem() -------------------------------------

DdeItem* DdeInternal::FindItem( DdeTopic& rTopic, HSZ hItem )
{
	DdeItem* s;
	DdeItems& rItems = rTopic.aItems;
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	int bWeiter = sal_False;

	do {            // middle check loop

		for ( s = rItems.First(); s; s = rItems.Next() )
			if ( *s->pName == hItem )
				return s;

		bWeiter = !bWeiter;
		if( !bWeiter )
			break;

		// dann befragen wir doch mal unsere Ableitung:
		TCHAR chBuf[250];
		DdeQueryString(pInst->hDdeInstSvr,hItem,chBuf,sizeof(chBuf)/sizeof(TCHAR),CP_WINUNICODE );
		bWeiter = rTopic.MakeItem( reinterpret_cast<const sal_Unicode*>(chBuf) );
		// dann muessen wir noch mal suchen
	} while( bWeiter );

	return 0;
}

// --- DdeService::DdeService() ------------------------------------

DdeService::DdeService( const String& rService )
{
	DdeInstData* pInst = ImpGetInstData();
	if( !pInst )
		pInst = ImpInitInstData();
	pInst->nRefCount++;
	pInst->nInstanceSvr++;

	if ( !pInst->hDdeInstSvr )
	{
		nStatus = sal::static_int_cast< short >(
            DdeInitialize( &pInst->hDdeInstSvr,
                           (PFNCALLBACK)DdeInternal::SvrCallback,
                           APPCLASS_STANDARD |
                           CBF_SKIP_REGISTRATIONS |
                           CBF_SKIP_UNREGISTRATIONS, 0L ) );
		pInst->pServicesSvr = new DdeServices;
	}
	else
		nStatus = DMLERR_NO_ERROR;

	pConv = new ConvList;

	if ( pInst->pServicesSvr )
		pInst->pServicesSvr->Insert( this );

	pName = new DdeString( pInst->hDdeInstSvr, rService );
	if ( nStatus == DMLERR_NO_ERROR )
		if ( !DdeNameService( pInst->hDdeInstSvr, *pName, NULL,
								DNS_REGISTER | DNS_FILTEROFF ) )
			nStatus = DMLERR_SYS_ERROR;

	AddFormat( FORMAT_STRING );
	pSysTopic = new DdeTopic( reinterpret_cast<const sal_Unicode*>(SZDDESYS_TOPIC) );
	pSysTopic->AddItem( DdeItem( reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_TOPICS) ) );
	pSysTopic->AddItem( DdeItem( reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_SYSITEMS) ) );
	pSysTopic->AddItem( DdeItem( reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_STATUS) ) );
	pSysTopic->AddItem( DdeItem( reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_FORMATS) ) );
	pSysTopic->AddItem( DdeItem( reinterpret_cast<const sal_Unicode*>(SZDDESYS_ITEM_HELP) ) );
	AddTopic( *pSysTopic );
}

// --- DdeService::~DdeService() -----------------------------------

DdeService::~DdeService()
{
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	if ( pInst->pServicesSvr )
		pInst->pServicesSvr->Remove( this );

	// MT: Im Auftrage des Herrn (AM) auskommentiert...
	// Grund:
	// Bei Client/Server werden die Server nicht beendet, wenn mehr
	// als einer gestartet.
	// Weil keine System-Messagequeue ?!

	delete pSysTopic;
	delete pName;

	pInst->nInstanceSvr--;
	pInst->nRefCount--;
	if ( !pInst->nInstanceSvr && pInst->hDdeInstSvr )
	{
		if( DdeUninitialize( pInst->hDdeInstSvr ) )
		{
			pInst->hDdeInstSvr = NULL;
			delete pInst->pServicesSvr;
			pInst->pServicesSvr = NULL;
			if( pInst->nRefCount == 0)
				ImpDeinitInstData();
		}
	}
	delete pConv;
}

// --- DdeService::GetName() ---------------------------------------

const String& DdeService::GetName() const
{
	return *pName;
}

// --- DdeService::GetServices() -----------------------------------

DdeServices& DdeService::GetServices()
{
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	return *(pInst->pServicesSvr);
}

// --- DdeService::AddTopic() --------------------------------------

void DdeService::AddTopic( const DdeTopic& rTopic )
{
	RemoveTopic( rTopic );
	aTopics.Insert( (DdeTopic*) &rTopic );
}

// --- DdeService::RemoveTopic() -----------------------------------

void DdeService::RemoveTopic( const DdeTopic& rTopic )
{
	DdeTopic* t;
	for ( t = aTopics.First(); t; t = aTopics.Next() )
	{
		if ( !DdeCmpStringHandles (*t->pName, *rTopic.pName ) )
		{
			aTopics.Remove( t );
			// JP 27.07.95: und alle Conversions loeschen !!!
			//              (sonst wird auf geloeschten Topics gearbeitet!!)
			for( sal_uLong n = pConv->Count(); n; )
			{
				Conversation* pC = pConv->GetObject( --n );
				if( pC->pTopic == &rTopic )
				{
					pConv->Remove( pC );
					delete pC;
				}
			}
			break;
		}
	}
}

// --- DdeService::HasCbFormat() -----------------------------------

sal_Bool DdeService::HasCbFormat( sal_uInt16 nFmt )
{
	return sal_Bool( aFormats.GetPos( nFmt ) != LIST_ENTRY_NOTFOUND );
}

// --- DdeService::HasFormat() -------------------------------------

sal_Bool DdeService::HasFormat( sal_uLong nFmt )
{
	return HasCbFormat( (sal_uInt16)DdeData::GetExternalFormat( nFmt ));
}

// --- DdeService::AddFormat() -------------------------------------

void DdeService::AddFormat( sal_uLong nFmt )
{
	nFmt = DdeData::GetExternalFormat( nFmt );
	aFormats.Remove( nFmt );
	aFormats.Insert( nFmt );
}

// --- DdeService::RemoveFormat() ----------------------------------

void DdeService::RemoveFormat( sal_uLong nFmt )
{
	aFormats.Remove( DdeData::GetExternalFormat( nFmt ) );
}

// --- DdeTopic::DdeTopic() ----------------------------------------

DdeTopic::DdeTopic( const String& rName )
{
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	pName = new DdeString( pInst->hDdeInstSvr, rName );
}

// --- DdeTopic::~DdeTopic() ---------------------------------------

DdeTopic::~DdeTopic()
{
	DdeItem* t;
	while( ( t = aItems.First() ) != NULL )
	{
		aItems.Remove( t );
		t->pMyTopic = 0;
		delete t;
	}
	delete pName;
}

// --- DdeTopic::GetName() -----------------------------------------

const String& DdeTopic::GetName() const
{
	return *pName;
}

// --- DdeTopic::IsSystemTopic() -----------------------------------

sal_Bool DdeTopic::IsSystemTopic()
{
	return sal_Bool (GetName() == reinterpret_cast<const sal_Unicode*>(SZDDESYS_TOPIC));
}

// --- DdeTopic::AddItem() -----------------------------------------

DdeItem* DdeTopic::AddItem( const DdeItem& r )
{
	DdeItem* s;
	if( DDEGETPUTITEM == r.nType )
		s = new DdeGetPutItem( r );
	else
		s = new DdeItem( r );
	if ( s )
	{
		aItems.Insert( s );
		s->pMyTopic = this;
	}
	return s;
}

// --- DdeTopic::InsertItem() -----------------------------------------

void DdeTopic::InsertItem( DdeItem* pNew )
{
	if( pNew )
	{
		aItems.Insert( pNew );
		pNew->pMyTopic = this;
	}
}

// --- DdeTopic::RemoveItem() --------------------------------------

void DdeTopic::RemoveItem( const DdeItem& r )
{
	DdeItem* s;
	for ( s = aItems.First(); s; s = aItems.Next() )
	{
		if ( !DdeCmpStringHandles (*s->pName, *r.pName ) )
			break;
	}

	if ( s )
	{
		aItems.Remove( s );
		s->pMyTopic = 0;
		delete s;
	}
}

// --- DdeTopic::NotifyClient() ------------------------------------

void DdeTopic::NotifyClient( const String& rItem )
{
	DdeItem* pItem;
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	for ( pItem = aItems.First(); pItem; pItem = aItems.Next() )
	{
		if ( pItem->GetName() == rItem )
		{
			if ( pItem->pImpData )
				DdePostAdvise( pInst->hDdeInstSvr, *pName, *pItem->pName );
		}
		break;
	}
}

// --- DdeTopic::Connect() -----------------------------------------

void __EXPORT DdeTopic::Connect( long nId )
{
	aConnectLink.Call( (void*)nId );
}

// --- DdeTopic::Disconnect() --------------------------------------

void __EXPORT DdeTopic::Disconnect( long nId )
{
	aDisconnectLink.Call( (void*)nId );
}

// --- DdeTopic::_Disconnect() --------------------------------------

void __EXPORT DdeTopic::_Disconnect( long nId )
{
	for( DdeItem* pItem = aItems.First(); pItem; pItem = aItems.Next() )
		pItem->DecMonitor( nId );

	Disconnect( nId );
}

// --- DdeTopic::Get() ---------------------------------------------

DdeData* __EXPORT DdeTopic::Get( sal_uLong nFmt )
{
	if ( aGetLink.IsSet() )
		return (DdeData*)aGetLink.Call( (void*)nFmt );
	else
		return NULL;
}

// --- DdeTopic::Put() ---------------------------------------------

sal_Bool __EXPORT DdeTopic::Put( const DdeData* r )
{
	if ( aPutLink.IsSet() )
		return (sal_Bool)aPutLink.Call( (void*) r );
	else
		return sal_False;
}

// --- DdeTopic::Execute() -----------------------------------------

sal_Bool __EXPORT DdeTopic::Execute( const String* r )
{
	if ( aExecLink.IsSet() )
		return (sal_Bool)aExecLink.Call( (void*)r );
	else
		return sal_False;
}

// --- DdeTopic::GetConvId() ---------------------------------------

long DdeTopic::GetConvId()
{
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	return pInst->hCurConvSvr;
}

// --- DdeTopic::StartAdviseLoop() ---------------------------------

sal_Bool DdeTopic::StartAdviseLoop()
{
	return sal_False;
}

// --- DdeTopic::StopAdviseLoop() ----------------------------------

sal_Bool DdeTopic::StopAdviseLoop()
{
	return sal_False;
}

// --- DdeItem::DdeItem() ------------------------------------------

DdeItem::DdeItem( const sal_Unicode* p )
{
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	pName = new DdeString( pInst->hDdeInstSvr, p );
	nType = DDEITEM;
	pMyTopic = 0;
	pImpData = 0;
}

// --- DdeItem::DdeItem() ------------------------------------------

DdeItem::DdeItem( const String& r)
{
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	pName = new DdeString( pInst->hDdeInstSvr, r );
	nType = DDEITEM;
	pMyTopic = 0;
	pImpData = 0;
}

// --- DdeItem::DdeItem() ------------------------------------------

DdeItem::DdeItem( const DdeItem& r)
{
	DdeInstData* pInst = ImpGetInstData();
	DBG_ASSERT(pInst,"SVDDE:No instance data");
	pName = new DdeString( pInst->hDdeInstSvr, *r.pName );
	nType = DDEITEM;
	pMyTopic = 0;
	pImpData = 0;
}

// --- DdeItem::~DdeItem() -----------------------------------------

DdeItem::~DdeItem()
{
	if( pMyTopic )
		pMyTopic->aItems.Remove( this );
	delete pName;
	delete pImpData;
}

// --- DdeItem::GetName() ------------------------------------------

const String& DdeItem::GetName() const
{
	return *pName;
}

// --- DdeItem::NotifyClient() ------------------------------------------

void DdeItem::NotifyClient()
{
	if( pMyTopic && pImpData )
	{
		DdeInstData* pInst = ImpGetInstData();
		DBG_ASSERT(pInst,"SVDDE:No instance data");
		DdePostAdvise( pInst->hDdeInstSvr, *pMyTopic->pName, *pName );
	}
}

// --- DdeItem::IncMonitor() ------------------------------------------

void DdeItem::IncMonitor( sal_uLong nHCnv )
{
	if( !pImpData )
	{
		pImpData = new DdeItemImp;
		if( DDEGETPUTITEM == nType )
			((DdeGetPutItem*)this)->AdviseLoop( sal_True );
	}
	else
	{
		for( sal_uInt16 n = pImpData->Count(); n; )
			if( (*pImpData)[ --n ].nHCnv == nHCnv )
			{
				++(*pImpData)[ n ].nHCnv;
				return ;
			}
	}

	pImpData->Insert( DdeItemImpData( nHCnv ), pImpData->Count() );
}

// --- DdeItem::DecMonitor() ------------------------------------------

void DdeItem::DecMonitor( sal_uLong nHCnv )
{
	if( pImpData )
	{
		DdeItemImpData* pData = (DdeItemImpData*)pImpData->GetData();
		for( sal_uInt16 n = pImpData->Count(); n; --n, ++pData )
			if( pData->nHCnv == nHCnv )
			{
				if( !pData->nCnt || !--pData->nCnt )
				{
					if( 1 < pImpData->Count() )
						pImpData->Remove( pImpData->Count() - n );
					else
					{
						delete pImpData, pImpData = 0;
						if( DDEGETPUTITEM == nType )
							((DdeGetPutItem*)this)->AdviseLoop( sal_False );
					}
				}
				return ;
			}
	}
}

// --- DdeItem::GetLinks() ------------------------------------------

short DdeItem::GetLinks()
{
	short nCnt = 0;
	if( pImpData )
		for( sal_uInt16 n = pImpData->Count(); n; )
			nCnt = nCnt + (*pImpData)[ --n ].nCnt;
	return nCnt;
}

// --- DdeGetPutItem::DdeGetPutItem() ------------------------------

DdeGetPutItem::DdeGetPutItem( const sal_Unicode* p )
	: DdeItem( p )
{
	nType = DDEGETPUTITEM;
}

// --- DdeGetPutItem::DdeGetPutItem() ------------------------------

DdeGetPutItem::DdeGetPutItem( const String& rStr )
	: DdeItem( rStr )
{
	nType = DDEGETPUTITEM;
}

// --- DdeGetPutItem::DdeGetPutItem() ------------------------------

DdeGetPutItem::DdeGetPutItem( const DdeItem& rItem )
	: DdeItem( rItem )
{
	nType = DDEGETPUTITEM;
}


// --- DdeGetPutData::Get() ----------------------------------------

DdeData* DdeGetPutItem::Get( sal_uLong )
{
	return 0;
}

// --- DdeGetPutData::Put() ----------------------------------------

sal_Bool DdeGetPutItem::Put( const DdeData* )
{
	return sal_False;
}

// --- DdeGetPutData::AdviseLoop() ---------------------------------

void DdeGetPutItem::AdviseLoop( sal_Bool )
{
}


// --- DdeService::SysItems() --------------------------------------

String DdeService::SysItems()
{
	String s;
	DdeTopic* t;
	for ( t = aTopics.First(); t; t = aTopics.Next() )
	{
		if ( t->GetName() == reinterpret_cast<const sal_Unicode*>(SZDDESYS_TOPIC) )
		{
			short n = 0;
			DdeItem* pi;
			for ( pi = t->aItems.First(); pi; pi = t->aItems.Next(), n++ )
			{
				if ( n )
					s += '\t';
				s += pi->GetName();
			}
			s += String::CreateFromAscii("\r\n");
		}
	}

	return s;
}

// --- DdeService::Topics() ----------------------------------------

String DdeService::Topics()
{
	String      s;
	DdeTopic*   t;
	short       n = 0;

	for ( t = aTopics.First(); t; t = aTopics.Next(), n++ )
	{
		if ( n )
			s += '\t';
		s += t->GetName();
	}
	s += String::CreateFromAscii("\r\n");

	return s;
}

// --- DdeService::Formats() ---------------------------------------

String DdeService::Formats()
{
	String      s;
	long        f;
	TCHAR       buf[128];
	LPCTSTR		p;
	short       n = 0;

	for ( f = aFormats.First(); f; f = aFormats.Next(), n++ )
	{
		if ( n )
			s += '\t';
		p = buf;

		switch( (sal_uInt16)f )
		{
			case CF_TEXT:
				p = reinterpret_cast<LPCTSTR>(String::CreateFromAscii("TEXT").GetBuffer());
				break;
			case CF_BITMAP:
				p = reinterpret_cast<LPCTSTR>(String::CreateFromAscii("BITMAP").GetBuffer());
				break;
#ifdef OS2
			case CF_DSPTEXT:
				p = String::CreateFromAscii("TEXT").GetBuffer();
				break;
			case CF_DSPBITMAP:
				p = String::CreateFromAscii("BITMAP").GetBuffer();
				break;
			case CF_METAFILE:
				p = String::CreateFromAscii("METAFILE").GetBuffer();
				break;
			case CF_DSPMETAFILE:
				p = String::CreateFromAscii("METAFILE").GetBuffer();
				break;
			case CF_PALETTE:
				p = String::CreateFromAscii("PALETTE").GetBuffer();
				break;
			default:
				p= String::CreateFromAscii("PRIVATE").GetBuffer();
#else
			default:
				GetClipboardFormatName( (UINT)f, buf, sizeof(buf) / sizeof(TCHAR) );
#endif
		}
		s += String( reinterpret_cast<const sal_Unicode*>(p) );
	}
	s += String::CreateFromAscii("\r\n");

	return s;
}

// --- DdeService::Status() ----------------------------------------

String DdeService::Status()
{
	return IsBusy() ? String::CreateFromAscii("Busy\r\n") : String::CreateFromAscii("Ready\r\n");
}

// --- DdeService::IsBusy() ----------------------------------------

sal_Bool __EXPORT DdeService::IsBusy()
{
	return sal_False;
}

// --- DdeService::GetHelp() ----------------------------------------

String __EXPORT DdeService::GetHelp()
{
	return String();
}

sal_Bool DdeTopic::MakeItem( const String& )
{
	return sal_False;
}

sal_Bool DdeService::MakeTopic( const String& )
{
	return sal_False;
}

String DdeService::SysTopicGet( const String& )
{
	return String();
}

sal_Bool DdeService::SysTopicExecute( const String* )
{
	return sal_False;
}

