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

#include <string.h>

#include "iprcache.hxx"
#include "linguistic/misc.hxx"

#include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp>
#include <tools/debug.hxx>
#include <osl/mutex.hxx>

//#define IPR_DEF_CACHE_SIZE		503
#define IPR_DEF_CACHE_MAX		375
#define IPR_DEF_CACHE_MAXINPUT	200

#ifdef DBG_STATISTIC
#include <tools/stream.hxx>

//#define IPR_CACHE_SIZE 		nTblSize
#define IPR_CACHE_MAX 		nMax
#define IPR_CACHE_MAXINPUT 	nMaxInput

#else

//#define IPR_CACHE_SIZE		IPR_DEF_CACHE_SIZE
#define IPR_CACHE_MAX		IPR_DEF_CACHE_MAX
#define IPR_CACHE_MAXINPUT	IPR_DEF_CACHE_MAXINPUT

#endif
#include <unotools/processfactory.hxx>

#include <linguistic/lngprops.hxx>

using namespace utl;
using namespace osl;
using namespace rtl;
using namespace com::sun::star;
using namespace com::sun::star::beans;
using namespace com::sun::star::lang;
using namespace com::sun::star::uno;
using namespace com::sun::star::linguistic2;


namespace linguistic
{

///////////////////////////////////////////////////////////////////////////

#define NUM_FLUSH_PROPS		6

static const struct
{
	const char *pPropName;
	sal_Int32		nPropHdl;
} aFlushProperties[ NUM_FLUSH_PROPS ] =
{
    { UPN_IS_USE_DICTIONARY_LIST,         UPH_IS_USE_DICTIONARY_LIST },
    { UPN_IS_IGNORE_CONTROL_CHARACTERS,   UPH_IS_IGNORE_CONTROL_CHARACTERS },
    { UPN_IS_SPELL_UPPER_CASE,            UPH_IS_SPELL_UPPER_CASE },
    { UPN_IS_SPELL_WITH_DIGITS,           UPH_IS_SPELL_WITH_DIGITS },
    { UPN_IS_SPELL_CAPITALIZATION,        UPH_IS_SPELL_CAPITALIZATION }
};


static void lcl_AddAsPropertyChangeListener(
		Reference< XPropertyChangeListener > xListener,
		Reference< XPropertySet > &rPropSet )
{
	if (xListener.is() && rPropSet.is())
	{
		for (int i = 0;  i < NUM_FLUSH_PROPS;  ++i)
		{
			rPropSet->addPropertyChangeListener(
					A2OU(aFlushProperties[i].pPropName), xListener );
		}
	}
}


static void lcl_RemoveAsPropertyChangeListener(
		Reference< XPropertyChangeListener > xListener,
		Reference< XPropertySet > &rPropSet )
{
	if (xListener.is() && rPropSet.is())
	{
		for (int i = 0;  i < NUM_FLUSH_PROPS;  ++i)
		{
			rPropSet->removePropertyChangeListener(
					A2OU(aFlushProperties[i].pPropName), xListener );
		}
	}
}


static sal_Bool lcl_IsFlushProperty( sal_Int32 nHandle )
{
	int i;
	for (i = 0;  i < NUM_FLUSH_PROPS;  ++i)
	{
		if (nHandle == aFlushProperties[i].nPropHdl)
			break;
	}
	return i < NUM_FLUSH_PROPS;
}


FlushListener::FlushListener( Flushable *pFO )
{
	SetFlushObj( pFO );
}


FlushListener::~FlushListener()
{
}


void FlushListener::SetDicList(	Reference<XDictionaryList> &rDL )
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (xDicList != rDL)
	{
		if (xDicList.is())
			xDicList->removeDictionaryListEventListener( this );

		xDicList = rDL;
		if (xDicList.is())
			xDicList->addDictionaryListEventListener( this, sal_False );
	}
}


void FlushListener::SetPropSet( Reference< XPropertySet > &rPS )
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (xPropSet != rPS)
	{
		if (xPropSet.is())
			lcl_RemoveAsPropertyChangeListener( this, xPropSet );

		xPropSet = rPS;
		if (xPropSet.is())
			lcl_AddAsPropertyChangeListener( this, xPropSet );
	}
}


void SAL_CALL FlushListener::disposing( const EventObject& rSource )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (xDicList.is()  &&  rSource.Source == xDicList)
	{
		xDicList->removeDictionaryListEventListener( this );
		xDicList = NULL;	//! release reference
	}
	if (xPropSet.is()  &&  rSource.Source == xPropSet)
	{
		lcl_RemoveAsPropertyChangeListener( this, xPropSet );
		xPropSet = NULL;	//! release reference
	}
}


void SAL_CALL FlushListener::processDictionaryListEvent(
			const DictionaryListEvent& rDicListEvent )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (rDicListEvent.Source == xDicList)
	{
		sal_Int16 nEvt = rDicListEvent.nCondensedEvent;
		sal_Int16 nFlushFlags =
				DictionaryListEventFlags::ADD_NEG_ENTRY		|
				DictionaryListEventFlags::DEL_POS_ENTRY		|
				DictionaryListEventFlags::ACTIVATE_NEG_DIC	|
				DictionaryListEventFlags::DEACTIVATE_POS_DIC;
		sal_Bool bFlush	= 0 != (nEvt & nFlushFlags);

		DBG_ASSERT( pFlushObj, "missing object (NULL pointer)" );
		if (bFlush && pFlushObj != NULL)
			pFlushObj->Flush();
	}
}


void SAL_CALL FlushListener::propertyChange(
			const PropertyChangeEvent& rEvt )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (rEvt.Source == xPropSet)
	{
		sal_Bool bFlush	= lcl_IsFlushProperty( rEvt.PropertyHandle );

		DBG_ASSERT( pFlushObj, "missing object (NULL pointer)" );
		if (bFlush && pFlushObj != NULL)
			pFlushObj->Flush();
	}
}


///////////////////////////////////////////////////////////////////////////

SpellCache::SpellCache()
{
	pFlushLstnr = new FlushListener( this );
	xFlushLstnr = pFlushLstnr;
    Reference<XDictionaryList> aDictionaryList(GetDictionaryList());
	pFlushLstnr->SetDicList( aDictionaryList );	//! after reference is established
    Reference<XPropertySet> aPropertySet(GetLinguProperties());
	pFlushLstnr->SetPropSet( aPropertySet );	//! after reference is established
}

SpellCache::~SpellCache()
{
    Reference<XDictionaryList>  aEmptyList;
    Reference<XPropertySet>     aEmptySet;
    pFlushLstnr->SetDicList( aEmptyList );
    pFlushLstnr->SetPropSet( aEmptySet );
}

void SpellCache::Flush()
{
    MutexGuard	aGuard( GetLinguMutex() );
    // clear word list
    LangWordList_t aEmpty;
    aWordLists.swap( aEmpty );
}

bool SpellCache::CheckWord( const OUString& rWord, LanguageType nLang )
{
    MutexGuard	aGuard( GetLinguMutex() );
    WordList_t &rList = aWordLists[ nLang ];
    const WordList_t::const_iterator aIt = rList.find( rWord );
    return aIt != rList.end();
}

void SpellCache::AddWord( const OUString& rWord, LanguageType nLang )
{
    MutexGuard	aGuard( GetLinguMutex() );
    WordList_t & rList = aWordLists[ nLang ];
    // occasional clean-up...
    if (rList.size() > 500)
        rList.clear();
    rList.insert( rWord );
}
///////////////////////////////////////////////////////////////////////////

}	// namespace linguistic
