/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/


#include "precompiled_formula.hxx"

#include "formula/formulahelper.hxx"
#include <unotools/charclass.hxx>
#include <unotools/syslocale.hxx>

namespace formula
{

    namespace 
    {
        //============================================================================
        class OEmptyFunctionDescription : public IFunctionDescription
        {
        public:
            OEmptyFunctionDescription(){}
            virtual ~OEmptyFunctionDescription(){}

            virtual ::rtl::OUString getFunctionName() const { return ::rtl::OUString(); }
            virtual const IFunctionCategory* getCategory() const { return NULL; }
            virtual ::rtl::OUString getDescription() const { return ::rtl::OUString(); }
            virtual xub_StrLen getSuppressedArgumentCount() const { return 0; }
            virtual ::rtl::OUString getFormula(const ::std::vector< ::rtl::OUString >& ) const { return ::rtl::OUString(); }
            virtual void fillVisibleArgumentMapping(::std::vector<sal_uInt16>& ) const {}
            virtual void initArgumentInfo()  const {}
            virtual ::rtl::OUString getSignature() const { return ::rtl::OUString(); }
            virtual rtl::OString getHelpId() const { return ""; }
            virtual sal_uInt32 getParameterCount() const { return 0; }
            virtual ::rtl::OUString getParameterName(sal_uInt32 ) const { return ::rtl::OUString(); }
            virtual ::rtl::OUString getParameterDescription(sal_uInt32 ) const { return ::rtl::OUString(); }
            virtual bool isParameterOptional(sal_uInt32 ) const { return sal_False; }
        };
    }
//===================================================================
//	class FormulaHelper - statische Methoden
//===================================================================

#define FUNC_NOTFOUND 0xffff

FormulaHelper::FormulaHelper(const IFunctionManager* _pFunctionManager) 
    :m_pSysLocale(new SvtSysLocale)
    ,m_pFunctionManager(_pFunctionManager)
    ,open(_pFunctionManager->getSingleToken(IFunctionManager::eOk))
    ,close(_pFunctionManager->getSingleToken(IFunctionManager::eClose))
    ,sep(_pFunctionManager->getSingleToken(IFunctionManager::eSep))
    ,arrayOpen(_pFunctionManager->getSingleToken(IFunctionManager::eArrayOpen))
    ,arrayClose(_pFunctionManager->getSingleToken(IFunctionManager::eArrayClose))
{
    m_pCharClass = m_pSysLocale->GetCharClassPtr();
}
sal_Bool FormulaHelper::GetNextFunc( const String&	rFormula,
								 sal_Bool			bBack,
								 xub_StrLen&	rFStart,   // Ein- und Ausgabe
								 xub_StrLen*	pFEnd, 	   // = NULL
								 const IFunctionDescription**	ppFDesc,   // = NULL
                                 ::std::vector< ::rtl::OUString>*	pArgs )  const // = NULL
{
	sal_Bool		bFound = sal_False;
	xub_StrLen	nOldStart = rFStart;
	String		aFname;

	rFStart = GetFunctionStart( rFormula, rFStart, bBack, ppFDesc ? &aFname : NULL );
	bFound  = ( rFStart != FUNC_NOTFOUND );

	if ( bFound )
	{
		if ( pFEnd )
			*pFEnd = GetFunctionEnd( rFormula, rFStart );

		if ( ppFDesc )
		{
            *ppFDesc = NULL;
            const ::rtl::OUString sTemp( aFname );
            const sal_uInt32 nCategoryCount = m_pFunctionManager->getCount();
            for(sal_uInt32 j= 0; j < nCategoryCount && !*ppFDesc; ++j)
            {
                const IFunctionCategory* pCategory = m_pFunctionManager->getCategory(j);
                const sal_uInt32 nCount = pCategory->getCount();
                for(sal_uInt32 i = 0 ; i < nCount; ++i)
                {
                    const IFunctionDescription* pCurrent = pCategory->getFunction(i);
	                if ( pCurrent->getFunctionName().equalsIgnoreAsciiCase(sTemp) )
                    {
                        *ppFDesc = pCurrent;
                        break;
                    }
                } // for(sal_uInt32 i = 0 ; i < nCount; ++i)
            }
			if ( *ppFDesc && pArgs )
			{
				GetArgStrings( *pArgs,rFormula, rFStart, static_cast<sal_uInt16>((*ppFDesc)->getParameterCount() ));
			}
            else
            {
                static OEmptyFunctionDescription s_aFunctionDescription;
                *ppFDesc = &s_aFunctionDescription;
            }
		}
	}
	else
		rFStart = nOldStart;

	return bFound;
}

//------------------------------------------------------------------------

void FormulaHelper::FillArgStrings( const String&	rFormula,
									xub_StrLen		nFuncPos,
									sal_uInt16			nArgs,
									::std::vector< ::rtl::OUString >& _rArgs ) const
{
	xub_StrLen	nStart	= 0;
	xub_StrLen	nEnd	= 0;
	sal_uInt16		i;
	sal_Bool		bLast	= sal_False;

	for ( i=0; i<nArgs && !bLast; i++ )
	{
		nStart = GetArgStart( rFormula, nFuncPos, i );

		if ( i+1<nArgs ) // letztes Argument?
		{
			nEnd = GetArgStart( rFormula, nFuncPos, i+1 );

			if ( nEnd != nStart )
                _rArgs.push_back(rFormula.Copy( nStart, nEnd-1-nStart ));
			else
				_rArgs.push_back(String()), bLast = sal_True;
		}
		else
		{
			nEnd = GetFunctionEnd( rFormula, nFuncPos )-1;
			if ( nStart < nEnd )
				_rArgs.push_back( rFormula.Copy( nStart, nEnd-nStart ) );
			else
				_rArgs.push_back(String());
		}
	}

	if ( bLast )
		for ( ; i<nArgs; i++ )
			_rArgs.push_back(String());
}

//------------------------------------------------------------------------

void FormulaHelper::GetArgStrings( ::std::vector< ::rtl::OUString >& _rArgs
                                      ,const String& rFormula,
									   xub_StrLen nFuncPos,
									   sal_uInt16 nArgs ) const
{
	if (nArgs)
	{
		FillArgStrings( rFormula, nFuncPos, nArgs, _rArgs );
	}
}

//------------------------------------------------------------------------

inline sal_Bool IsFormulaText( const CharClass* _pCharClass,const String& rStr, xub_StrLen nPos )
{
	if( _pCharClass->isLetterNumeric( rStr, nPos ) )
		return sal_True;
	else
	{	// In internationalized versions function names may contain a dot
		//  and in every version also an underscore... ;-)
		sal_Unicode c = rStr.GetChar(nPos);
		return c == '.' || c == '_';
	}

}

xub_StrLen FormulaHelper::GetFunctionStart( const String&	rFormula,
										xub_StrLen		nStart,
										sal_Bool			bBack,
										String*			pFuncName ) const
{
	xub_StrLen nStrLen = rFormula.Len();

	if ( nStrLen < nStart )
		return nStart;

	xub_StrLen	nFStart = FUNC_NOTFOUND;
	xub_StrLen	nParPos	= nStart;

	sal_Bool bRepeat, bFound;
	do
	{
		bFound  = sal_False;
		bRepeat = sal_False;

		if ( bBack )
		{
			while ( !bFound && (nParPos > 0) )
			{
				if ( rFormula.GetChar(nParPos) == '"' )
				{
					nParPos--;
					while ( (nParPos > 0) && rFormula.GetChar(nParPos) != '"' )
						nParPos--;
					if (nParPos > 0)
						nParPos--;
				}
                else if ( (bFound = ( rFormula.GetChar(nParPos) == '(' ) ) == sal_False )
					nParPos--;
			}
		}
		else
		{
			while ( !bFound && (nParPos < nStrLen) )
			{
				if ( rFormula.GetChar(nParPos) == '"' )
				{
					nParPos++;
					while ( (nParPos < nStrLen) && rFormula.GetChar(nParPos) != '"' )
						nParPos++;
					nParPos++;
				}
                else if ( (bFound = ( rFormula.GetChar(nParPos) == '(' ) ) == sal_False )
					nParPos++;
			}
		}

		if ( bFound && (nParPos > 0) )
		{
			nFStart = nParPos-1;

			while ( (nFStart > 0) && IsFormulaText(m_pCharClass, rFormula, nFStart ))
				nFStart--;
		}

		nFStart++;

		if ( bFound )
		{
			if ( IsFormulaText( m_pCharClass,rFormula, nFStart ) )
			{
									//	Funktion gefunden
				if ( pFuncName )
					*pFuncName = rFormula.Copy( nFStart, nParPos-nFStart );
			}
			else					// Klammern ohne Funktion -> weitersuchen
			{
				bRepeat = sal_True;
				if ( !bBack )
					nParPos++;
				else if (nParPos > 0)
					nParPos--;
				else
					bRepeat = sal_False;
			}
		}
		else						// keine Klammern gefunden
		{
			nFStart = FUNC_NOTFOUND;
			if ( pFuncName )
				pFuncName->Erase();
		}
	}
	while(bRepeat);

	return nFStart;
}

//------------------------------------------------------------------------

xub_StrLen	FormulaHelper::GetFunctionEnd( const String& rStr, xub_StrLen nStart ) const
{
	xub_StrLen nStrLen = rStr.Len();

	if ( nStrLen < nStart )
		return nStart;

	short	nParCount = 0;
    bool    bInArray = false;
	sal_Bool	bFound = sal_False;

	while ( !bFound && (nStart < nStrLen) )
	{
		sal_Unicode c = rStr.GetChar(nStart);

		if ( c == '"' )
		{
			nStart++;
			while ( (nStart < nStrLen) && rStr.GetChar(nStart) != '"' )
				nStart++;
		}
		else if ( c == open )
			nParCount++;
		else if ( c == close )
		{
			nParCount--;
			if ( nParCount == 0 )
				bFound = sal_True;
			else if ( nParCount < 0 )
			{
				bFound = sal_True;
				nStart--;	// einen zu weit gelesen
			}
		}
        else if ( c == arrayOpen )
        {
            bInArray = true;
        }
        else if ( c == arrayClose )
        {
            bInArray = false;
        }
		else if ( c == sep )
		{
			if ( !bInArray && nParCount == 0 )
			{
				bFound = sal_True;
				nStart--;	// einen zu weit gelesen
			}
		}
		nStart++; // hinter gefundene Position stellen
	}

	return nStart;
}

//------------------------------------------------------------------

xub_StrLen FormulaHelper::GetArgStart( const String& rStr, xub_StrLen nStart, sal_uInt16 nArg ) const
{
	xub_StrLen nStrLen = rStr.Len();

	if ( nStrLen < nStart )
		return nStart;

	short	nParCount	= 0;
    bool    bInArray    = false;
	sal_Bool	bFound		= sal_False;

	while ( !bFound && (nStart < nStrLen) )
	{
		sal_Unicode c = rStr.GetChar(nStart);

		if ( c == '"' )
		{
			nStart++;
			while ( (nStart < nStrLen) && rStr.GetChar(nStart) != '"' )
				nStart++;
		}
		else if ( c == open )
		{
			bFound = ( nArg == 0 );
			nParCount++;
		}
		else if ( c == close )
		{
			nParCount--;
			bFound = ( nParCount == 0 );
		}
        else if ( c == arrayOpen )
        {
            bInArray = true;
        }
        else if ( c == arrayClose )
        {
            bInArray = false;
        }
		else if ( c == sep )
		{
			if ( !bInArray && nParCount == 1 )
			{
				nArg--;
				bFound = ( nArg == 0  );
			}
		}
		nStart++;
	}

	return nStart;
}
// =============================================================================
} // formula
// =============================================================================
