/*************************************************************************
 *
 *  Authors:
 *  Sun Microsystems Inc., October, 2000
 *  Pasi Ryhänen, 2002/08/15
 *  Harri Pitkänen (hatapitk@cc.jyu.fi), 2005
 *
 *  The Contents of this file are made available subject to the terms of
 *  GNU Lesser General Public License Version 2.1
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA  02110-1301, USA
 *
 ************************************************************************/

#include <cppuhelper/factory.hxx>
#include <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
#include <osl/file.hxx>

#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>

#include "hyphenimp.hxx"
#include "commonfi.hxx"

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 soikko {

Hyphenator::Hyphenator() :
  aEvtListeners	( GetLinguMutex() )
{
  SPDLOG(("Hyphenator()\n"));
  init_done = false;
  dl.handle = NULL;
  hyphenator_handle = NULL;
  instPath = getInstallationPath();
  bDisposing = FALSE;
}


Hyphenator::~Hyphenator()
{
  SPDLOG(("~Hyphenator()\n"));
}


Sequence< Locale > SAL_CALL 
Hyphenator::getLocales()
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );
  SPDLOG(("Hyphenator::getLocales()\n"));
	
  if (!aSuppLocales.getLength())
    {

      aSuppLocales.realloc( 1 );
      Locale *pLocale = aSuppLocales.getArray();
      pLocale[0] = Locale( A2OU("fi"), A2OU("FI"), OUString() );

    }

  return aSuppLocales;
}


sal_Bool SAL_CALL 
Hyphenator::hasLocale(const Locale& rLocale)
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );
  
  SPDLOG(("Hyphenator::hasLocale()\n"));
  sal_Bool bRes = FALSE;
  if (!aSuppLocales.getLength())
    getLocales();

  sal_Int32 nLen = aSuppLocales.getLength();
  for (sal_Int32 i = 0;  i < nLen;  ++i)
    {
      const Locale *pLocale = aSuppLocales.getConstArray();
      if (rLocale == pLocale[i])
	{
	  bRes = TRUE;
	  break;
	}
    }
  return bRes;
}


Reference< XHyphenatedWord > SAL_CALL 
Hyphenator::hyphenate( const ::rtl::OUString& aWord,
		       const ::com::sun::star::lang::Locale& aLocale,
		       sal_Int16 nMaxLeading, 
		       const ::com::sun::star::beans::PropertyValues& aProperties ) 
  throw(::com::sun::star::lang::IllegalArgumentException,
	::com::sun::star::uno::RuntimeException)
{ 
  SPDLOG(("Hyphenator::hyphenate()\n"));
  if (!init_done)
    init_dl();

  int nHyphenationPos = -1;
  int wordlen;
  char *hyphens;
  int i;

  OString encWord(OU2ISO_1(aWord));

  OUString tohyphenate;
  Reference< XHyphenatedWord > xRes;

  wordlen = encWord.getLength();

  hyphens = new char[wordlen+2];

  SPDLOG(("  word %s\n", encWord.getStr()));
  if (hyphenator_handle) {
    dl.hyphenate_func(hyphenator_handle, encWord.getStr(), hyphens);
  }
  OUStringBuffer hyphenatedWord;
  sal_Int32 Leading = GetPosInWordToCheck( aWord, nMaxLeading );

  for (i = 0; i < wordlen; i++)
    {
      if ((hyphens[i + 1] == '^')  && (i < Leading))
	{
	  nHyphenationPos = i;
	}
    }
  SPDLOG((" pos %d, Leading %d\n", nHyphenationPos, Leading));
  // TODO: removing the apostrophe (vaa'an -> vaa-an) is the only special case

  if (nHyphenationPos == -1)
    xRes = NULL;
  else
    {
      xRes = new HyphenatedWord(aWord, nHyphenationPos);
    }

  delete[] hyphens;
  return xRes;
}


Reference < XHyphenatedWord > SAL_CALL
Hyphenator::queryAlternativeSpelling( const ::rtl::OUString& aWord,
				      const ::com::sun::star::lang::Locale& aLocale,
				      sal_Int16 nIndex,
				      const ::com::sun::star::beans::PropertyValues& aProperties )
  throw(::com::sun::star::lang::IllegalArgumentException,
	::com::sun::star::uno::RuntimeException)
{
  SPDLOG(("Hyphenator::queryAlternativeSpellint()\n"));
  return NULL;
}


// Find possible hyphenation points for the user.
// The user selects the best one.
Reference< XPossibleHyphens > SAL_CALL
Hyphenator::createPossibleHyphens( const ::rtl::OUString& aWord,
				   const ::com::sun::star::lang::Locale& aLocale,
				   const ::com::sun::star::beans::PropertyValues& aProperties )
  throw(::com::sun::star::lang::IllegalArgumentException,
	::com::sun::star::uno::RuntimeException)

{
  int i;
  SPDLOG(("Hyphenator::createPossibleHyphens\n"));
  
  int nHyphenationPos = -1;
  int wordlen;
  char *hyphens;
  
  OString encWord;
  OUString tohyphenate;
  Reference< XPossibleHyphens > xRes;
  
  encWord = OU2ISO_15(aWord);
  wordlen = encWord.getLength();
  hyphens = new char[wordlen + 2];

  SPDLOG(("  word %s\n", encWord.getStr()));
  if (hyphenator_handle) {
    dl.hyphenate_func(hyphenator_handle, encWord.getStr(), hyphens);
  }

  Sequence< sal_Int16 > aHyphPos( wordlen );
  sal_Int16 *pPos = aHyphPos.getArray();
  OUStringBuffer hyphenatedWordBuffer;
  OUString hyphenatedWord;
  sal_Int16 nHyphCount = 0;

  for (i = 0; i < wordlen; i++)
    {
      hyphenatedWordBuffer.append(aWord[i]);
      if (hyphens[i + 1] == '^')
	{
	  pPos[nHyphCount] = i;
	  hyphenatedWordBuffer.append(sal_Unicode('='));
	  nHyphCount++;
	}
    }

  hyphenatedWord = hyphenatedWordBuffer.makeStringAndClear();
  
  xRes = new PossibleHyphens( aWord, hyphenatedWord, aHyphPos );

  delete hyphens;
  return xRes;
}


OUString SAL_CALL 
Hyphenator::getServiceDisplayName( const Locale& rLocale ) 
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );
  if (rLocale == Locale( A2OU("fi"), A2OU("FI"), OUString() ))
    return A2OU( "Suomen kielen tavutus (Soikko)" );
  else
    return A2OU( "Finnish hyphenator (Soikko)" );
}


void SAL_CALL 
Hyphenator::initialize( const Sequence< Any >& rArguments ) 
  throw(Exception, RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );

  SPDLOG(("Hyphenator::initialize()\n"));
  init_dl();
}


void
Hyphenator::init_dl()
{
  int st;

  init_done = true;

  OUString dict_path = instPath.concat(A2OU( SOIKKO_HYPHD ));
  
  OString dict_str(OU2ISO_1(dict_path));
  SPDLOG(("dictionary %s\n", dict_str.getStr()));
  
  OString dll_name(OU2ISO_1(instPath.concat(A2OU( SOIKKO_LIB ))));

  st = load_dl(dll_name.getStr(), &dl);
  if (st)
    goto ERROR;
  st = dl.init_func(&hyphenator_handle);
  if (st)
    goto ERROR;
  st = dl.openhyph_func(hyphenator_handle, dict_str.getStr());
  if (st) {
    dl.terminate_func(hyphenator_handle);
    goto ERROR;
  }
  
  return;

 ERROR:
  SPDLOG(("initialize() failed\n"));
  hyphenator_handle = NULL;
  close_dl(&dl);
  return;
}





void SAL_CALL 
Hyphenator::dispose() 
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );

  SPDLOG(("Hyphenator::dispose()\n"));
  if (!bDisposing)
    {
      bDisposing = TRUE;
      EventObject	aEvtObj( (XHyphenator *) this );
      aEvtListeners.disposeAndClear( aEvtObj );
    }
  if (hyphenator_handle) {
    dl.terminate_func(hyphenator_handle);
    hyphenator_handle = NULL;
  }
  close_dl(&dl);
}


void SAL_CALL 
Hyphenator::addEventListener( const Reference< XEventListener >& rxListener ) 
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );

  if (!bDisposing && rxListener.is())
    aEvtListeners.addInterface( rxListener );
}


void SAL_CALL 
Hyphenator::removeEventListener( const Reference< XEventListener >& rxListener ) 
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );
	
  if (!bDisposing && rxListener.is())
    aEvtListeners.removeInterface( rxListener );
}


///////////////////////////////////////////////////////////////////////////
// Service specific part
//

OUString SAL_CALL Hyphenator::getImplementationName() 
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );

  return getImplementationName_Static();
}


sal_Bool SAL_CALL Hyphenator::supportsService( const OUString& ServiceName )
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );

  Sequence< OUString > aSNL = getSupportedServiceNames();
  const OUString * pArray = aSNL.getConstArray();
  for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
    if( pArray[i] == ServiceName )
      return TRUE;
  return FALSE;
}


Sequence< OUString > SAL_CALL Hyphenator::getSupportedServiceNames()
  throw(RuntimeException)
{
  MutexGuard	aGuard( GetLinguMutex() );

  return getSupportedServiceNames_Static();
}


Sequence< OUString > Hyphenator::getSupportedServiceNames_Static() 
  throw()
{
  MutexGuard	aGuard( GetLinguMutex() );

  Sequence< OUString > aSNS( 1 );
  aSNS.getArray()[0] = A2OU( "com.sun.star.linguistic2.Hyphenator" );
  return aSNS;
}

}

