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


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

#include <tools/debug.hxx>
#include <string.h>
#include <memory>
#include <unotools/collatorwrapper.hxx>
#include <unotools/transliterationwrapper.hxx>

#include "token.hxx"
#include "tokenarray.hxx"
#include "rangenam.hxx"
#include "global.hxx"
#include "compiler.hxx"
#include "rangeutl.hxx"
#include "rechead.hxx"
#include "refupdat.hxx"
#include "document.hxx"

using namespace formula;

//========================================================================
// ScRangeData
//========================================================================

// Interner ctor fuer das Suchen nach einem Index

ScRangeData::ScRangeData( sal_uInt16 n )
           : pCode( NULL ), nIndex( n ), bModified( sal_False ), mnMaxRow(-1), mnMaxCol(-1), aRangeNameScope( MAXTABCOUNT )
{}

ScRangeData::ScRangeData( ScDocument* pDok,
						  const String& rName,
						  const String& rSymbol,
                          const ScAddress& rAddress,
						  RangeType nType,
						  const FormulaGrammar::Grammar eGrammar ) :
				aName		( rName ),
                aUpperName  ( ScGlobal::pCharClass->upper( rName ) ),
				pCode		( NULL ),
				aPos		( rAddress ),
				eType		( nType ),
				pDoc		( pDok ),
				nIndex		( 0 ),
                bModified	( sal_False ),
                mnMaxRow    (-1),
                mnMaxCol    (-1),
				aRangeNameScope( MAXTABCOUNT )
{
	if (rSymbol.Len() > 0)
	{
		ScCompiler aComp( pDoc, aPos );
        aComp.SetGrammar(eGrammar);
		pCode = aComp.CompileString( rSymbol );
		if( !pCode->GetCodeError() )
		{
			pCode->Reset();
			FormulaToken* p = pCode->GetNextReference();
			if( p )// genau eine Referenz als erstes
			{
				if( p->GetType() == svSingleRef )
					eType = eType | RT_ABSPOS;
				else
					eType = eType | RT_ABSAREA;
			}
			// ggf. den Fehlercode wg. unvollstaendiger Formel setzen!
			// Dies ist fuer die manuelle Eingabe
			aComp.CompileTokenArray();
			pCode->DelRPN();
		}
	}
    else
    {
        // #i63513#/#i65690# don't leave pCode as NULL.
        // Copy ctor default-constructs pCode if it was NULL, so it's initialized here, too,
        // to ensure same behavior if unnecessary copying is left out.

        pCode = new ScTokenArray();
    }
}

ScRangeData::ScRangeData( ScDocument* pDok,
						  const String& rName,
						  const ScTokenArray& rArr,
                          const ScAddress& rAddress,
						  RangeType nType ) :
				aName		( rName ),
                aUpperName  ( ScGlobal::pCharClass->upper( rName ) ),
				pCode		( new ScTokenArray( rArr ) ),
				aPos		( rAddress ),
				eType		( nType ),
				pDoc		( pDok ),
				nIndex		( 0 ),
                bModified	( sal_False ),
                mnMaxRow    (-1),
                mnMaxCol    (-1),
				aRangeNameScope( MAXTABCOUNT )
{
	if( !pCode->GetCodeError() )
	{
		pCode->Reset();
		FormulaToken* p = pCode->GetNextReference();
		if( p )// genau eine Referenz als erstes
		{
			if( p->GetType() == svSingleRef )
				eType = eType | RT_ABSPOS;
			else
				eType = eType | RT_ABSAREA;
		}
		// Die Importfilter haben diesen Test nicht,
		// da die benannten Bereiche z.T. noch unvollstaendig sind.
//		if( !pCode->GetCodeLen() )
//		{
//			// ggf. den Fehlercode wg. unvollstaendiger Formel setzen!
//			ScCompiler aComp( pDok, aPos, *pCode );
//			aComp.CompileTokenArray();
//			pCode->DelRPN();
//		}
	}
}

ScRangeData::ScRangeData( ScDocument* pDok,
						  const String& rName,
						  const ScAddress& rTarget ) :
				aName		( rName ),
                aUpperName  ( ScGlobal::pCharClass->upper( rName ) ),
				pCode		( new ScTokenArray() ),
				aPos		( rTarget ),
				eType		( RT_NAME ),
				pDoc		( pDok ),
				nIndex		( 0 ),
                bModified	( sal_False ),
                mnMaxRow    (-1),
                mnMaxCol    (-1),
				aRangeNameScope( MAXTABCOUNT )
{
	ScSingleRefData aRefData;
	aRefData.InitAddress( rTarget );
	aRefData.SetFlag3D( sal_True );
	pCode->AddSingleReference( aRefData );
	ScCompiler aComp( pDoc, aPos, *pCode );
    aComp.SetGrammar(pDoc->GetGrammar());
	aComp.CompileTokenArray();
	if ( !pCode->GetCodeError() )
		eType |= RT_ABSPOS;
}

ScRangeData::ScRangeData(const ScRangeData& rScRangeData) :
    ScDataObject(),
	aName 	(rScRangeData.aName),
    aUpperName  (rScRangeData.aUpperName),
	pCode		(rScRangeData.pCode ? rScRangeData.pCode->Clone() : new ScTokenArray()),		// echte Kopie erzeugen (nicht copy-ctor)
	aPos		(rScRangeData.aPos),
	eType		(rScRangeData.eType),
	pDoc		(rScRangeData.pDoc),
	nIndex   	(rScRangeData.nIndex),
    bModified	(rScRangeData.bModified),
    mnMaxRow    (rScRangeData.mnMaxRow),
    mnMaxCol    (rScRangeData.mnMaxCol),
	aRangeNameScope (rScRangeData.aRangeNameScope)
{}

ScRangeData::~ScRangeData()
{
	delete pCode;
}

ScDataObject* ScRangeData::Clone() const
{
	return new ScRangeData(*this);
}

void ScRangeData::GuessPosition()
{
	//	setzt eine Position, mit der alle relative Referenzen bei CalcAbsIfRel
	//	ohne Fehler verabsolutiert werden koennen

	DBG_ASSERT(aPos == ScAddress(), "die Position geht jetzt verloren");

	SCsCOL nMinCol = 0;
	SCsROW nMinRow = 0;
	SCsTAB nMinTab = 0;

	ScToken* t;
	pCode->Reset();
    while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
	{
		ScSingleRefData& rRef1 = t->GetSingleRef();
		if ( rRef1.IsColRel() && rRef1.nRelCol < nMinCol )
			nMinCol = rRef1.nRelCol;
		if ( rRef1.IsRowRel() && rRef1.nRelRow < nMinRow )
			nMinRow = rRef1.nRelRow;
		if ( rRef1.IsTabRel() && rRef1.nRelTab < nMinTab )
			nMinTab = rRef1.nRelTab;

		if ( t->GetType() == svDoubleRef )
		{
			ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
			if ( rRef2.IsColRel() && rRef2.nRelCol < nMinCol )
				nMinCol = rRef2.nRelCol;
			if ( rRef2.IsRowRel() && rRef2.nRelRow < nMinRow )
				nMinRow = rRef2.nRelRow;
			if ( rRef2.IsTabRel() && rRef2.nRelTab < nMinTab )
				nMinTab = rRef2.nRelTab;
		}
	}

	aPos = ScAddress( (SCCOL)(-nMinCol), (SCROW)(-nMinRow), (SCTAB)(-nMinTab) );

	//!	Test
//	DBG_ERROR(String("Pos ")+String((SCCOL)(-nMinCol))+String("/")+
//			String((SCROW)(-nMinRow))+String("/")+String((SCTAB)(-nMinTab)));
}

void ScRangeData::GetSymbol( String& rSymbol, const FormulaGrammar::Grammar eGrammar ) const
{
	ScCompiler aComp(pDoc, aPos, *pCode);
    aComp.SetGrammar(eGrammar);
	aComp.CreateStringFromTokenArray( rSymbol );
}

void ScRangeData::UpdateSymbol(	rtl::OUStringBuffer& rBuffer, const ScAddress& rPos,
								const FormulaGrammar::Grammar eGrammar )
{
    ::std::auto_ptr<ScTokenArray> pTemp( pCode->Clone() );
	ScCompiler aComp( pDoc, rPos, *pTemp.get());
    aComp.SetGrammar(eGrammar);
    aComp.MoveRelWrap(GetMaxCol(), GetMaxRow());
	aComp.CreateStringFromTokenArray( rBuffer );
}

void ScRangeData::UpdateReference(	UpdateRefMode eUpdateRefMode,
									const ScRange& r,
									SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
	sal_Bool bChanged = sal_False;

	pCode->Reset();
	if( pCode->GetNextReference() )
	{
        sal_Bool bSharedFormula = ((eType & RT_SHARED) == RT_SHARED);
		ScCompiler aComp( pDoc, aPos, *pCode );
        aComp.SetGrammar(pDoc->GetGrammar());
		const sal_Bool bRelRef = aComp.UpdateNameReference( eUpdateRefMode, r,
													nDx, nDy, nDz,
													bChanged, bSharedFormula);
		if (bSharedFormula)
		{
			if (bRelRef)
				eType = eType | RT_SHAREDMOD;
			else
				eType = eType & ~RT_SHAREDMOD;
		}
	}

	bModified = bChanged;
}


void ScRangeData::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest )
{
	sal_Bool bChanged = sal_False;

	ScToken* t;
	pCode->Reset();

	while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
	{
		if( t->GetType() != svIndex )
		{
			SingleDoubleRefModifier aMod( *t );
			ScComplexRefData& rRef = aMod.Ref();
			if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() &&
					(!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) &&
				( t->GetType() == svSingleRef ||
				(!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() &&
					(!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel()))))
			{
				if ( ScRefUpdate::UpdateTranspose( pDoc, rSource, rDest, rRef ) != UR_NOTHING )
					bChanged = sal_True;
			}
		}
	}

	bModified = bChanged;
}

void ScRangeData::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
{
	sal_Bool bChanged = sal_False;

	ScToken* t;
	pCode->Reset();

	while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
	{
		if( t->GetType() != svIndex )
		{
			SingleDoubleRefModifier aMod( *t );
			ScComplexRefData& rRef = aMod.Ref();
			if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() &&
					(!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) &&
				( t->GetType() == svSingleRef ||
				(!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() &&
					(!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel()))))
			{
				if ( ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, rRef ) != UR_NOTHING )
					bChanged = sal_True;
			}
		}
	}

	bModified = bChanged;			// muss direkt hinterher ausgewertet werden
}

sal_Bool ScRangeData::operator== (const ScRangeData& rData) const		// fuer Undo
{
	if ( nIndex	!= rData.nIndex	||
		 aName	!= rData.aName	||
		 aPos	!= rData.aPos	||
		 eType	!= rData.eType   || aRangeNameScope  != rData.aRangeNameScope  ) return sal_False;

	sal_uInt16 nLen = pCode->GetLen();
	if ( nLen != rData.pCode->GetLen() ) return sal_False;

	FormulaToken** ppThis = pCode->GetArray();
	FormulaToken** ppOther = rData.pCode->GetArray();

	for ( sal_uInt16 i=0; i<nLen; i++ )
		if ( ppThis[i] != ppOther[i] && !(*ppThis[i] == *ppOther[i]) )
			return sal_False;

	return sal_True;
}

//UNUSED2009-05 sal_Bool ScRangeData::IsRangeAtCursor( const ScAddress& rPos, sal_Bool bStartOnly ) const
//UNUSED2009-05 {
//UNUSED2009-05     sal_Bool bRet = sal_False;
//UNUSED2009-05     ScRange aRange;
//UNUSED2009-05     if ( IsReference(aRange) )
//UNUSED2009-05     {
//UNUSED2009-05         if ( bStartOnly )
//UNUSED2009-05             bRet = ( rPos == aRange.aStart );
//UNUSED2009-05         else
//UNUSED2009-05             bRet = ( aRange.In( rPos ) );
//UNUSED2009-05     }
//UNUSED2009-05     return bRet;
//UNUSED2009-05 }

sal_Bool ScRangeData::IsRangeAtBlock( const ScRange& rBlock ) const
{
	sal_Bool bRet = sal_False;
	ScRange aRange;
	if ( IsReference(aRange) )
		bRet = ( rBlock == aRange );
	return bRet;
}

sal_Bool ScRangeData::IsReference( ScRange& rRange ) const
{
	if ( (eType & ( RT_ABSAREA | RT_REFAREA | RT_ABSPOS )) && pCode )
		return pCode->IsReference( rRange );

    return sal_False;
}

sal_Bool ScRangeData::IsReference( ScRange& rRange, const ScAddress& rPos ) const
{
	if ( (eType & ( RT_ABSAREA | RT_REFAREA | RT_ABSPOS ) ) && pCode )
    {
        ::std::auto_ptr<ScTokenArray> pTemp( pCode->Clone() );
        ScCompiler aComp( pDoc, rPos, *pTemp);
        aComp.SetGrammar(pDoc->GetGrammar());
        aComp.MoveRelWrap(MAXCOL, MAXROW);
        return pTemp->IsReference( rRange );
    }

    return sal_False;
}

sal_Bool ScRangeData::IsValidReference( ScRange& rRange ) const
{
    if ( (eType & ( RT_ABSAREA | RT_REFAREA | RT_ABSPOS ) ) && pCode )
        return pCode->IsValidReference( rRange );

    return sal_False;
}

/* modification to update named range scope */
void ScRangeData::UpdateTabRef(SCTAB nOldTable, sal_uInt16 nFlag, SCTAB nNewTable)
{
	pCode->Reset();
	if( pCode->GetNextReference() )
	{
        ScRangeData* pRangeData = NULL;     // must not be dereferenced
		sal_Bool bChanged;
		ScCompiler aComp( pDoc, aPos, *pCode);
        aComp.SetGrammar(pDoc->GetGrammar());
		switch (nFlag)
		{
			case 1:										// einfache InsertTab (doc.cxx)
			case 4:	
			       pRangeData = aComp.UpdateInsertTab(nOldTable, true );	// und CopyTab (doc2.cxx)
				if ( (aRangeNameScope != MAXTABCOUNT) && ( aRangeNameScope >= nOldTable) && ( aRangeNameScope != MAXTAB ) )
					aRangeNameScope ++;
				break;
			case 2:										// einfaches delete (doc.cxx)
				pRangeData = aComp.UpdateDeleteTab(nOldTable, false, true, bChanged);
				if  ( aRangeNameScope != MAXTABCOUNT && aRangeNameScope > nOldTable )
                                    aRangeNameScope --;
				break;
			case 3:										// move (doc2.cxx)
			{
				pRangeData = aComp.UpdateMoveTab(nOldTable, nNewTable, true );
				if ( aRangeNameScope != MAXTABCOUNT )
				{
					if ( aRangeNameScope == nOldTable )
						aRangeNameScope = nNewTable;
					else if ( (aRangeNameScope > nOldTable) && (aRangeNameScope <= nNewTable) )
						aRangeNameScope--;
					else if ( (aRangeNameScope >= nNewTable) && (aRangeNameScope < nOldTable) )
						aRangeNameScope++;
				}
			}
			break;
			case 5:										
			{
                                //when copying a sheet, this will be invoked to update the new name range's address in the new sheet 
                                //only need to update the address if the address's tab same as the range scope. because if they are different, the address's tab have been updated in ScRangeName::UpdateTabRef()  
                                //for example, in sheet5(scope is sheet5), there are two name range, one address is sheet5, the other is sheet4, if copy sheet5 to sheet1
                                //only need to change the first one's address to sheet1
				pRangeData = aComp.UpdateMoveTab(nOldTable, nNewTable, true , true);
				aRangeNameScope = nNewTable;
			}
			break;
			default:
			{
				DBG_ERROR("ScRangeName::UpdateTabRef: Unknown Flag");
			}
				break;
		}
		if (eType&RT_SHARED)
		{
			if (pRangeData)
				eType = eType | RT_SHAREDMOD;
			else
				eType = eType & ~RT_SHAREDMOD;
		}
	}
}


void ScRangeData::MakeValidName( String& rName )		// static
{
    //ScCompiler::InitSymbolsNative();

    // strip leading invalid characters
	xub_StrLen nPos = 0;
	xub_StrLen nLen = rName.Len();
	while ( nPos < nLen && !ScCompiler::IsCharFlagAllConventions( rName, nPos, SC_COMPILER_C_NAME) )
		++nPos;
	if ( nPos>0 )
		rName.Erase(0,nPos);

    // if the first character is an invalid start character, precede with '_'	
	if ( rName.Len() && !ScCompiler::IsCharFlagAllConventions( rName, 0, SC_COMPILER_C_CHAR_NAME ) )
		rName.Insert('_',0);

    // replace invalid with '_'
	nLen = rName.Len();
	for (nPos=0; nPos<nLen; nPos++)
	{
		if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos, SC_COMPILER_C_NAME) )
			rName.SetChar( nPos, '_' );
	}

    // Ensure that the proposed name is not a reference under any convention, 
    // same as in IsNameValid()
	ScAddress aAddr;
	ScRange aRange;
    for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; )
    {
        ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) );
        // Don't check Parse on VALID, any partial only VALID may result in 
        // #REF! during compile later!
        while (aRange.Parse( rName, NULL, details) || aAddr.Parse( rName, NULL, details))
        {
            //! Range Parse is partially valid also with invalid sheet name,
            //! Address Parse dito, during compile name would generate a #REF!
            if ( rName.SearchAndReplace( '.', '_' ) == STRING_NOTFOUND )
                rName.Insert('_',0);
        }
    }
}

sal_Bool ScRangeData::IsNameValid( const String& rName, ScDocument* pDoc )
{
    /* XXX If changed, sc/source/filter/ftools/ftools.cxx 
     * ScfTools::ConvertToScDefinedName needs to be changed too. */
	xub_StrLen nPos = 0;
	xub_StrLen nLen = rName.Len();
	if ( !nLen || !ScCompiler::IsCharFlagAllConventions( rName, nPos++, SC_COMPILER_C_CHAR_NAME ) )
		return sal_False;
	while ( nPos < nLen )
	{
		if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos++, SC_COMPILER_C_NAME ) )
			return sal_False;
	}
    ScAddress aAddr;
	ScRange aRange;
    for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; )
    {
        ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) );
        // Don't check Parse on VALID, any partial only VALID may result in 
        // #REF! during compile later!
        if (aRange.Parse( rName, pDoc, details) || aAddr.Parse( rName, pDoc, details))
		    return sal_False;
    }
	return sal_True;
}

void ScRangeData::SetMaxRow(SCROW nRow)
{
    mnMaxRow = nRow;
}

SCROW ScRangeData::GetMaxRow() const
{
    return mnMaxRow >= 0 ? mnMaxRow : MAXROW;
}

void ScRangeData::SetMaxCol(SCCOL nCol)
{
    mnMaxCol = nCol;
}

SCCOL ScRangeData::GetMaxCol() const
{
    return mnMaxCol >= 0 ? mnMaxCol : MAXCOL;
}

/* MAXTABCOUNT - Global, 0 - sheet1, 1 - sheet2, ...		*/
/*	MAXTABCOUNT -- Global				*/
/*	return value: FALSE -- set fail		 	*/
/*			      TRUE  -- set successfully	*/
bool ScRangeData::SetRangeScope( SCTAB Scope )
{
	 if ( Scope <= MAXTABCOUNT && Scope >=0 )
	 {
		aRangeNameScope = Scope;
	        return true;
	 }
         return false;
	
}

 String ScRangeData::GetScopeSheetName() const
{
	if ( aRangeNameScope != MAXTABCOUNT )
	{
            String aTableName;
            pDoc->GetName( aRangeNameScope, aTableName );
            return aTableName;
	}
        return EMPTY_STRING;
}
/* end add */


sal_uInt16 ScRangeData::GetErrCode()
{
	return pCode ? pCode->GetCodeError() : 0;
}

sal_Bool ScRangeData::HasReferences() const
{
	pCode->Reset();
	return sal_Bool( pCode->GetNextReference() != NULL );
}

// bei TransferTab von einem in ein anderes Dokument anpassen,
// um Referenzen auf die eigene Tabelle mitzubekommen

void ScRangeData::TransferTabRef( SCTAB nOldTab, SCTAB nNewTab )
{
	long nTabDiff = (long)nNewTab - nOldTab;
	long nPosDiff = (long)nNewTab - aPos.Tab();
	aPos.SetTab( nNewTab );
	ScToken* t;
	pCode->Reset();
    while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
	{
		ScSingleRefData& rRef1 = t->GetSingleRef();
		if ( rRef1.IsTabRel() )
            rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + nPosDiff );
		else
            rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + nTabDiff );
		if ( t->GetType() == svDoubleRef )
		{
			ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
			if ( rRef2.IsTabRel() )
                rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + nPosDiff );
			else
                rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + nTabDiff );
		}
	}
}

void ScRangeData::ReplaceRangeNamesInUse( const IndexMap& rMap )
{
    bool bCompile = false;
    for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
    {
        if ( p->GetOpCode() == ocName )
        {
            const sal_uInt16 nOldIndex = p->GetIndex();
            IndexMap::const_iterator itr = rMap.find(nOldIndex);
            const sal_uInt16 nNewIndex = itr == rMap.end() ? nOldIndex : itr->second;
            if ( nOldIndex != nNewIndex )
            {
                p->SetIndex( nNewIndex );
                bCompile = true;
            }
        }
    }
    if ( bCompile )
    {
        ScCompiler aComp( pDoc, aPos, *pCode);
        aComp.SetGrammar(pDoc->GetGrammar());
        aComp.CompileTokenArray();
    }
}


void ScRangeData::ValidateTabRefs()
{
	//	try to make sure all relative references and the reference position
	//	are within existing tables, so they can be represented as text
	//	(if the range of used tables is more than the existing tables,
	//	the result may still contain invalid tables, because the relative
	//	references aren't changed so formulas stay the same)

	//	find range of used tables

	SCTAB nMinTab = aPos.Tab();
	SCTAB nMaxTab = nMinTab;
	ScToken* t;
	pCode->Reset();
    while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
	{
		ScSingleRefData& rRef1 = t->GetSingleRef();
		if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() )
		{
			if ( rRef1.nTab < nMinTab )
				nMinTab = rRef1.nTab;
			if ( rRef1.nTab > nMaxTab )
				nMaxTab = rRef1.nTab;
		}
		if ( t->GetType() == svDoubleRef )
		{
			ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
			if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() )
			{
				if ( rRef2.nTab < nMinTab )
					nMinTab = rRef2.nTab;
				if ( rRef2.nTab > nMaxTab )
					nMaxTab = rRef2.nTab;
			}
		}
	}

	SCTAB nTabCount = pDoc->GetTableCount();
	if ( nMaxTab >= nTabCount && nMinTab > 0 )
	{
		//	move position and relative tab refs
		//	The formulas that use the name are not changed by this

		SCTAB nMove = nMinTab;
		aPos.SetTab( aPos.Tab() - nMove );

		pCode->Reset();
        while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
		{
			ScSingleRefData& rRef1 = t->GetSingleRef();
			if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() )
                rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab - nMove );
			if ( t->GetType() == svDoubleRef )
			{
				ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
				if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() )
                    rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab - nMove );
			}
		}
	}
}


extern "C" int
#ifdef WNT
__cdecl
#endif
ScRangeData_QsortNameCompare( const void* p1, const void* p2 )
{
	return (int) ScGlobal::GetCollator()->compareString(
			(*(const ScRangeData**)p1)->GetName(),
			(*(const ScRangeData**)p2)->GetName() );
}


//========================================================================
// ScRangeName
//========================================================================

ScRangeName::ScRangeName(const ScRangeName& rScRangeName, ScDocument* pDocument) :
				ScSortedCollection ( rScRangeName ),
				pDoc ( pDocument ),
				nSharedMaxIndex (rScRangeName.nSharedMaxIndex)
{
	for (sal_uInt16 i = 0; i < nCount; i++)
	{
		((ScRangeData*)At(i))->SetDocument(pDocument);
		((ScRangeData*)At(i))->SetIndex(((ScRangeData*)rScRangeName.At(i))->GetIndex());
	}
}

short ScRangeName::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const
{
	sal_uInt16 i1 = ((ScRangeData*)pKey1)->GetIndex();
	sal_uInt16 i2 = ((ScRangeData*)pKey2)->GetIndex();
	return (short) i1 - (short) i2;
}

/* added  for scope support */
bool ScRangeName::HasRangeinSheetScope(SCTAB Scope)
{
    for (sal_uInt16 i = 0; i < nCount; i++)
        if  (((*this)[i])->GetRangeScope() == Scope) 
            return true;

    return false;
}
/* if Scope is global, no range will be removed */
/* if no range is removed, return value is false */
bool ScRangeName::RemoveRangeinScope(SCTAB Scope)
{
     bool bRemoved = false;
	
     if ( Scope == MAXTABCOUNT )
         return bRemoved;

     sal_uInt16 i = 0;
     while (i < nCount)
     {
          if  (((*this)[i])->GetRangeScope() == Scope) 
          {
               Free( (*this)[i] );
               bRemoved = true;		
          }
          else
               i++;
     }

     return bRemoved;
}
/* it's designed for "Copy Sheet" action. So no name conflict check when copy range to new scope */
/* if the old scope or the new scope is global, no range will be copied */
/* if no range is copied, the return value is false */
bool ScRangeName::CopyRangeinScope(SCTAB oldScope, SCTAB newScope)
{
       bool bCopied = false;

       if ( (oldScope == MAXTABCOUNT)||(newScope ==MAXTABCOUNT) )
           return bCopied;

       sal_uInt16 originalCount = nCount;
       for ( sal_uInt16 i = 0; i < originalCount; i++)
           if ( ((*this)[i])->GetRangeScope() == oldScope) 
           {
                 ScRangeData * aCopiedRange = (ScRangeData *)(*this)[i]->Clone();
                 aCopiedRange->UpdateTabRef(oldScope, 5 , newScope);
                 aCopiedRange->SetIndex(GetEntryIndex());
                 Insert( aCopiedRange );
                 bCopied = true;		
           }

       return bCopied;
}
/* end add */
bool ScRangeName::SearchNameUpper( const String& rUpperName, sal_uInt16& rIndex, SCTAB Scope ) const
{
    // SearchNameUpper must be called with an upper-case search string

    sal_uInt16 i = 0;
    while (i < nCount)
    {
        if ( (((*this)[i])->GetUpperName() == rUpperName) 
			&& (((*this)[i])->GetRangeScope() == Scope ))
        {
            rIndex = i;
            return true;
        }
        i++;
    }
    return false;
}

bool ScRangeName::SearchName( const String& rName, sal_uInt16& rIndex, SCTAB Scope ) const
{
    if ( nCount > 0 )
        return SearchNameUpper( ScGlobal::pCharClass->upper( rName ), rIndex, Scope );
    else
        return false;
}

void ScRangeName::UpdateReference(	UpdateRefMode eUpdateRefMode,
									const ScRange& rRange,
									SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
	for (sal_uInt16 i=0; i<nCount; i++)
		((ScRangeData*)pItems[i])->UpdateReference(eUpdateRefMode, rRange,
												   nDx, nDy, nDz);
}

void ScRangeName::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest )
{
	for (sal_uInt16 i=0; i<nCount; i++)
		((ScRangeData*)pItems[i])->UpdateTranspose( rSource, rDest );
}

void ScRangeName::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
{
	for (sal_uInt16 i=0; i<nCount; i++)
		((ScRangeData*)pItems[i])->UpdateGrow( rArea, nGrowX, nGrowY );
}

sal_Bool ScRangeName::IsEqual(ScDataObject* pKey1, ScDataObject* pKey2) const
{
	return *(ScRangeData*)pKey1 == *(ScRangeData*)pKey2;
}

sal_Bool ScRangeName::Insert(ScDataObject* pScDataObject)
{
	if (!((ScRangeData*)pScDataObject)->GetIndex())		// schon gesetzt?
	{
		((ScRangeData*)pScDataObject)->SetIndex( GetEntryIndex() );
	}

	return ScSortedCollection::Insert(pScDataObject);
}

// Suche nach einem freien Index

sal_uInt16 ScRangeName::GetEntryIndex()
{
	sal_uInt16 nLast = 0;
	for ( sal_uInt16 i = 0; i < nCount; i++ )
	{
		sal_uInt16 nIdx = ((ScRangeData*)pItems[i])->GetIndex();
		if( nIdx > nLast )
		{
			nLast = nIdx;
		}
	}
	return nLast + 1;
}

ScRangeData* ScRangeName::FindIndex( sal_uInt16 nIndex )
{
	ScRangeData aDataObj( nIndex );
	sal_uInt16 n;
	if( Search( &aDataObj, n ) )
		return (*this)[ n ];
	else
		return NULL;
}

//UNUSED2009-05 ScRangeData* ScRangeName::GetRangeAtCursor( const ScAddress& rPos, sal_Bool bStartOnly ) const
//UNUSED2009-05 {
//UNUSED2009-05     if ( pItems )
//UNUSED2009-05     {
//UNUSED2009-05         for ( sal_uInt16 i = 0; i < nCount; i++ )
//UNUSED2009-05             if ( ((ScRangeData*)pItems[i])->IsRangeAtCursor( rPos, bStartOnly ) )
//UNUSED2009-05                 return (ScRangeData*)pItems[i];
//UNUSED2009-05     }
//UNUSED2009-05     return NULL;
//UNUSED2009-05 }

ScRangeData* ScRangeName::GetRangeAtBlock( const ScRange& rBlock ) const
{
	if ( pItems )
	{
		for ( sal_uInt16 i = 0; i < nCount; i++ )
			if ( ((ScRangeData*)pItems[i])->IsRangeAtBlock( rBlock ) )
				return (ScRangeData*)pItems[i];
	}
	return NULL;
}

void ScRangeName::UpdateTabRef(SCTAB nOldTable, sal_uInt16 nFlag, SCTAB nNewTable)
{
       if (nFlag == 2)
           RemoveRangeinScope( nOldTable );

       for (sal_uInt16 i=0; i<nCount; i++)
            ((ScRangeData*)pItems[i])->UpdateTabRef(nOldTable, nFlag, nNewTable);

       if (nFlag ==4)
       {
            SCTAB copyScope = nOldTable > nNewTable ? nNewTable : nNewTable+1;
            CopyRangeinScope( copyScope, nOldTable);
       }
}



