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

#include <tools/debug.hxx>

#include "consoli.hxx"
#include "document.hxx"
#include "olinetab.hxx"
#include "globstr.hrc"
#include "subtotal.hxx"
#include "formula/errorcodes.hxx"
#include "cell.hxx"

#include <math.h>
#include <string.h>

#define SC_CONS_NOTFOUND	-1

// STATIC DATA -----------------------------------------------------------

/*	Strings bei Gelegenheit ganz raus...
static sal_uInt16 nFuncRes[] = {				//	Reihenfolge wie bei enum ScSubTotalFunc
		0,									//	none
		STR_PIVOTFUNC_AVG,
		STR_PIVOTFUNC_COUNT,
		STR_PIVOTFUNC_COUNT2,
		STR_PIVOTFUNC_MAX,
		STR_PIVOTFUNC_MIN,
		STR_PIVOTFUNC_PROD,
		STR_PIVOTFUNC_STDDEV,
		STR_PIVOTFUNC_STDDEV2,
		STR_PIVOTFUNC_SUM,
		STR_PIVOTFUNC_VAR,
		STR_PIVOTFUNC_VAR2 };
*/

static OpCode eOpCodeTable[] = {			//	Reihenfolge wie bei enum ScSubTotalFunc
		ocBad,								//	none
		ocAverage,
		ocCount,
		ocCount2,
		ocMax,
		ocMin,
		ocProduct,
		ocStDev,
		ocStDevP,
		ocSum,
		ocVar,
		ocVarP };

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

void ScReferenceList::AddEntry( SCCOL nCol, SCROW nRow, SCTAB nTab )
{
	ScReferenceEntry* pOldData = pData;
	pData = new ScReferenceEntry[ nFullSize+1 ];
	if (pOldData)
	{
		memmove( pData, pOldData, nCount * sizeof(ScReferenceEntry) );
		delete[] pOldData;
	}
	while (nCount < nFullSize)
	{
		pData[nCount].nCol = SC_CONS_NOTFOUND;
		pData[nCount].nRow = SC_CONS_NOTFOUND;
		pData[nCount].nTab = SC_CONS_NOTFOUND;
		++nCount;
	}
	pData[nCount].nCol = nCol;
	pData[nCount].nRow = nRow;
	pData[nCount].nTab = nTab;
	++nCount;
	nFullSize = nCount;
}

template< typename T >
void lcl_AddString( String**& pData, T& nCount, const String& rInsert )
{
	String** pOldData = pData;
	pData = new String*[ nCount+1 ];
	if (pOldData)
	{
		memmove( pData, pOldData, nCount * sizeof(String*) );
		delete[] pOldData;
	}
	pData[nCount] = new String(rInsert);
	++nCount;
}

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

ScConsData::ScConsData() :
	eFunction(SUBTOTAL_FUNC_SUM),
	bReference(sal_False),
	bColByName(sal_False),
	bRowByName(sal_False),
	bSubTitles(sal_False),
	nColCount(0),
	nRowCount(0),
	ppUsed(NULL),
	ppSum(NULL),
	ppCount(NULL),
	ppSumSqr(NULL),
	ppRefs(NULL),
	ppColHeaders(NULL),
	ppRowHeaders(NULL),
	nDataCount(0),
	nTitleCount(0),
	ppTitles(NULL),
	ppTitlePos(NULL),
	bCornerUsed(sal_False)
{
}

ScConsData::~ScConsData()
{
	DeleteData();
}


#define DELETEARR(ppArray,nCount)	\
{									\
	sal_uLong i; 						\
	if (ppArray) 					\
		for(i=0; i<nCount; i++)		\
			delete[] ppArray[i];	\
	delete[] ppArray;				\
	ppArray = NULL;					\
}

#define DELETESTR(ppArray,nCount)	\
{									\
	sal_uLong i; 						\
	if (ppArray) 					\
		for(i=0; i<nCount; i++)		\
			delete ppArray[i];		\
	delete[] ppArray;				\
	ppArray = NULL;					\
}

void ScConsData::DeleteData()
{
	if (ppRefs)
    {
		for (SCSIZE i=0; i<nColCount; i++)
		{
			for (SCSIZE j=0; j<nRowCount; j++)
				if (ppUsed[i][j])
					ppRefs[i][j].Clear();
			delete[] ppRefs[i];
		}
	    delete[] ppRefs;
	    ppRefs = NULL;
    }

//	DELETEARR( ppData1, nColCount );
//	DELETEARR( ppData2, nColCount );
	DELETEARR( ppCount, nColCount );
	DELETEARR( ppSum,   nColCount );
	DELETEARR( ppSumSqr,nColCount );
	DELETEARR( ppUsed,  nColCount );				// erst nach ppRefs !!!
	DELETEARR( ppTitlePos, nRowCount );
	DELETESTR( ppColHeaders, nColCount );
	DELETESTR( ppRowHeaders, nRowCount );
	DELETESTR( ppTitles, nTitleCount );
	nTitleCount = 0;
	nDataCount = 0;

	if (bColByName) nColCount = 0;					// sonst stimmt ppColHeaders nicht
	if (bRowByName) nRowCount = 0;

	bCornerUsed = sal_False;
	aCornerText.Erase();
}

#undef DELETEARR
#undef DELETESTR

void ScConsData::InitData( sal_Bool bDelete )
{
	if (bDelete)
		DeleteData();

	if (bReference && nColCount && !ppRefs)
	{
		ppRefs = new ScReferenceList*[nColCount];
		for (SCSIZE i=0; i<nColCount; i++)
			ppRefs[i] = new ScReferenceList[nRowCount];
	}
	else if (nColCount && !ppCount)
	{
		ppCount  = new double*[nColCount];
		ppSum    = new double*[nColCount];
		ppSumSqr = new double*[nColCount];
		for (SCSIZE i=0; i<nColCount; i++)
		{
			ppCount[i]  = new double[nRowCount];
			ppSum[i]    = new double[nRowCount];
			ppSumSqr[i] = new double[nRowCount];
		}
	}

	if (nColCount && !ppUsed)
	{
		ppUsed = new sal_Bool*[nColCount];
		for (SCSIZE i=0; i<nColCount; i++)
		{
			ppUsed[i] = new sal_Bool[nRowCount];
			memset( ppUsed[i], 0, nRowCount * sizeof(sal_Bool) );
		}
	}

	if (nRowCount && nDataCount && !ppTitlePos)
	{
		ppTitlePos = new SCSIZE*[nRowCount];
		for (SCSIZE i=0; i<nRowCount; i++)
		{
			ppTitlePos[i] = new SCSIZE[nDataCount];
			memset( ppTitlePos[i], 0, nDataCount * sizeof(SCSIZE) );	//! unnoetig ?
		}
	}

	//	CornerText: einzelner String
}

void ScConsData::DoneFields()
{
	InitData(sal_False);
}

void ScConsData::SetSize( SCCOL nCols, SCROW nRows )
{
	DeleteData();
	nColCount = static_cast<SCSIZE>(nCols);
	nRowCount = static_cast<SCSIZE>(nRows);
}

void ScConsData::GetSize( SCCOL& rCols, SCROW& rRows ) const
{
	rCols = static_cast<SCCOL>(nColCount);
	rRows = static_cast<SCROW>(nRowCount);
}

void ScConsData::SetFlags( ScSubTotalFunc eFunc, sal_Bool bColName, sal_Bool bRowName, sal_Bool bRef )
{
	DeleteData();
	bReference = bRef;
	bColByName = bColName;
	if (bColName) nColCount = 0;
	bRowByName = bRowName;
	if (bRowName) nRowCount = 0;
	eFunction = eFunc;
}

void ScConsData::AddFields( ScDocument* pSrcDoc, SCTAB nTab,
							SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
	++nDataCount;

	String aTitle;

	SCCOL nStartCol = nCol1;
	SCROW nStartRow = nRow1;
	if (bColByName)	++nStartRow;
	if (bRowByName)	++nStartCol;

	if (bColByName)
	{
		for (SCCOL nCol=nStartCol; nCol<=nCol2; nCol++)
		{
			pSrcDoc->GetString( nCol, nRow1, nTab, aTitle );
			if (aTitle.Len())
			{
				sal_Bool bFound = sal_False;
				for (SCSIZE i=0; i<nColCount && !bFound; i++)
					if ( *ppColHeaders[i] == aTitle )
						bFound = sal_True;
				if (!bFound)
					lcl_AddString( ppColHeaders, nColCount, aTitle );
			}
		}
	}

	if (bRowByName)
	{
		for (SCROW nRow=nStartRow; nRow<=nRow2; nRow++)
		{
			pSrcDoc->GetString( nCol1, nRow, nTab, aTitle );
			if (aTitle.Len())
			{
				sal_Bool bFound = sal_False;
				for (SCSIZE i=0; i<nRowCount && !bFound; i++)
					if ( *ppRowHeaders[i] == aTitle )
						bFound = sal_True;
				if (!bFound)
					lcl_AddString( ppRowHeaders, nRowCount, aTitle );
			}
		}
	}
}

void ScConsData::AddName( const String& rName )
{
	SCSIZE nArrX;
	SCSIZE nArrY;

	if (bReference)
	{
		lcl_AddString( ppTitles, nTitleCount, rName );

		for (nArrY=0; nArrY<nRowCount; nArrY++)
		{
			//	Daten auf gleiche Laenge bringen

			SCSIZE nMax = 0;
			for (nArrX=0; nArrX<nColCount; nArrX++)
				if (ppUsed[nArrX][nArrY])
					nMax = Max( nMax, ppRefs[nArrX][nArrY].GetCount() );

			for (nArrX=0; nArrX<nColCount; nArrX++)
			{
				if (!ppUsed[nArrX][nArrY])
				{
					ppUsed[nArrX][nArrY] = sal_True;
					ppRefs[nArrX][nArrY].Init();
				}
				ppRefs[nArrX][nArrY].SetFullSize(nMax);
			}

			//	Positionen eintragen

			if (ppTitlePos)
				if (nTitleCount < nDataCount)
					ppTitlePos[nArrY][nTitleCount] = nMax;
		}
	}
}

								// rCount < 0 <=> Fehler aufgetreten

void lcl_UpdateArray( ScSubTotalFunc eFunc,
						 double& rCount, double& rSum, double& rSumSqr, double nVal )
{
	if (rCount < 0.0)
		return;
	switch (eFunc)
	{
		case SUBTOTAL_FUNC_SUM:
			if (!SubTotal::SafePlus(rSum, nVal))
				rCount = -MAXDOUBLE;
			break;
		case SUBTOTAL_FUNC_PROD:
			if (!SubTotal::SafeMult(rSum, nVal))
				rCount = -MAXDOUBLE;
			break;
		case SUBTOTAL_FUNC_CNT:
		case SUBTOTAL_FUNC_CNT2:
			rCount += 1.0;
			break;
		case SUBTOTAL_FUNC_AVE:
			if (!SubTotal::SafePlus(rSum, nVal))
				rCount = -MAXDOUBLE;
			else
				rCount += 1.0;
			break;
		case SUBTOTAL_FUNC_MAX:
			if (nVal > rSum)
				rSum = nVal;
			break;
		case SUBTOTAL_FUNC_MIN:
			if (nVal < rSum)
				rSum = nVal;
			break;
		case SUBTOTAL_FUNC_STD:
		case SUBTOTAL_FUNC_STDP:
		case SUBTOTAL_FUNC_VAR:
		case SUBTOTAL_FUNC_VARP:
		{
			sal_Bool bOk = SubTotal::SafePlus(rSum, nVal);
			bOk = bOk && SubTotal::SafeMult(nVal, nVal);
			bOk = bOk && SubTotal::SafePlus(rSumSqr, nVal);
			if (!bOk)
				rCount = -MAXDOUBLE;
			else
				rCount += 1.0;
			break;
		}
        default:
        {
            // added to avoid warnings
        }
	}
}

void lcl_InitArray( ScSubTotalFunc eFunc,
					   double& rCount, double& rSum, double& rSumSqr, double nVal )
{
	rCount = 1.0;
	switch (eFunc)
	{
		case SUBTOTAL_FUNC_SUM:
		case SUBTOTAL_FUNC_MAX:
		case SUBTOTAL_FUNC_MIN:
		case SUBTOTAL_FUNC_PROD:
		case SUBTOTAL_FUNC_AVE:
			rSum = nVal;
			break;
		case SUBTOTAL_FUNC_STD:
		case SUBTOTAL_FUNC_STDP:
		case SUBTOTAL_FUNC_VAR:
		case SUBTOTAL_FUNC_VARP:
		{
			rSum = nVal;
			sal_Bool bOk = SubTotal::SafeMult(nVal, nVal);
			if (bOk)
				rSumSqr = nVal;
			else
				rCount = -MAXDOUBLE;
		}
			break;
		default:
			break;
	}
}

double lcl_CalcData( ScSubTotalFunc eFunc,
						double fCount, double fSum, double fSumSqr)
{
	if (fCount < 0.0)
		return 0.0;
	double fVal = 0.0;
	switch (eFunc)
	{
		case SUBTOTAL_FUNC_CNT:
		case SUBTOTAL_FUNC_CNT2:
			fVal = fCount;
			break;
		case SUBTOTAL_FUNC_SUM:
		case SUBTOTAL_FUNC_MAX:
		case SUBTOTAL_FUNC_MIN:
		case SUBTOTAL_FUNC_PROD:
			fVal = fSum;
			break;
		case SUBTOTAL_FUNC_AVE:
			if (fCount > 0.0)
				fVal = fSum / fCount;
			else
				fCount = -MAXDOUBLE;
			break;
		case SUBTOTAL_FUNC_STD:
		{
			if (fCount > 1 && SubTotal::SafeMult(fSum, fSum))
				fVal = sqrt((fSumSqr - fSum/fCount)/(fCount-1.0));
			else
				fCount = -MAXDOUBLE;
		}
			break;
		case SUBTOTAL_FUNC_STDP:
		{
			if (fCount > 0 && SubTotal::SafeMult(fSum, fSum))
				fVal = sqrt((fSumSqr - fSum/fCount)/fCount);
			else
				fCount = -MAXDOUBLE;
		}
			break;
		case SUBTOTAL_FUNC_VAR:
		{
			if (fCount > 1 && SubTotal::SafeMult(fSum, fSum))
				fVal = (fSumSqr - fSum/fCount)/(fCount-1.0);
			else
				fCount = -MAXDOUBLE;
		}
			break;
		case SUBTOTAL_FUNC_VARP:
		{
			if (fCount > 0 && SubTotal::SafeMult(fSum, fSum))
				fVal = (fSumSqr - fSum/fCount)/fCount;
			else
				fCount = -MAXDOUBLE;
		}
			break;
		default:
		{
			DBG_ERROR("unbekannte Funktion bei Consoli::CalcData");
			fCount = -MAXDOUBLE;
		}
			break;
	}
	return fVal;
}

void ScConsData::AddData( ScDocument* pSrcDoc, SCTAB nTab,
							SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
	PutInOrder(nCol1,nCol2);
	PutInOrder(nRow1,nRow2);
    if ( nCol2 >= sal::static_int_cast<SCCOL>(nCol1 + nColCount) && !bColByName )
	{
		DBG_ASSERT(0,"Bereich zu gross");
        nCol2 = sal::static_int_cast<SCCOL>( nCol1 + nColCount - 1 );
	}
    if ( nRow2 >= sal::static_int_cast<SCROW>(nRow1 + nRowCount) && !bRowByName )
	{
		DBG_ASSERT(0,"Bereich zu gross");
        nRow2 = sal::static_int_cast<SCROW>( nRow1 + nRowCount - 1 );
	}

	SCCOL nCol;
	SCROW nRow;

	//		Ecke links oben

	if ( bColByName && bRowByName )
	{
		String aThisCorner;
		pSrcDoc->GetString(nCol1,nRow1,nTab,aThisCorner);
		if (bCornerUsed)
		{
			if (aCornerText != aThisCorner)
				aCornerText.Erase();
		}
		else
		{
			aCornerText = aThisCorner;
			bCornerUsed = sal_True;
		}
	}

	//		Titel suchen

	SCCOL nStartCol = nCol1;
	SCROW nStartRow = nRow1;
	if (bColByName)	++nStartRow;
	if (bRowByName)	++nStartCol;
	String aTitle;
	SCCOL*	pDestCols = NULL;
	SCROW*	pDestRows = NULL;
	if (bColByName)
	{
		pDestCols = new SCCOL[nCol2-nStartCol+1];
		for (nCol=nStartCol; nCol<=nCol2; nCol++)
		{
			pSrcDoc->GetString(nCol,nRow1,nTab,aTitle);
			SCCOL nPos = SC_CONS_NOTFOUND;
			if (aTitle.Len())
			{
				sal_Bool bFound = sal_False;
				for (SCSIZE i=0; i<nColCount && !bFound; i++)
					if ( *ppColHeaders[i] == aTitle )
					{
						nPos = static_cast<SCCOL>(i);
						bFound = sal_True;
					}
				DBG_ASSERT(bFound, "Spalte nicht gefunden");
			}
			pDestCols[nCol-nStartCol] = nPos;
		}
	}
	if (bRowByName)
	{
		pDestRows = new SCROW[nRow2-nStartRow+1];
		for (nRow=nStartRow; nRow<=nRow2; nRow++)
		{
			pSrcDoc->GetString(nCol1,nRow,nTab,aTitle);
			SCROW nPos = SC_CONS_NOTFOUND;
			if (aTitle.Len())
			{
				sal_Bool bFound = sal_False;
				for (SCSIZE i=0; i<nRowCount && !bFound; i++)
					if ( *ppRowHeaders[i] == aTitle )
					{
						nPos = static_cast<SCROW>(i);
						bFound = sal_True;
					}
				DBG_ASSERT(bFound, "Zeile nicht gefunden");
			}
			pDestRows[nRow-nStartRow] = nPos;
		}
	}
	nCol1 = nStartCol;
	nRow1 = nStartRow;

	//		Daten

	sal_Bool bAnyCell = ( eFunction == SUBTOTAL_FUNC_CNT2 );
	for (nCol=nCol1; nCol<=nCol2; nCol++)
	{
		SCCOL nArrX = nCol-nCol1;
		if (bColByName)	nArrX = pDestCols[nArrX];
		if (nArrX != SC_CONS_NOTFOUND)
		{
			for (nRow=nRow1; nRow<=nRow2; nRow++)
			{
				SCROW nArrY = nRow-nRow1;
				if (bRowByName)	nArrY = pDestRows[nArrY];
				if ( nArrY != SC_CONS_NOTFOUND && (
						bAnyCell ? pSrcDoc->HasData( nCol, nRow, nTab )
								 : pSrcDoc->HasValueData( nCol, nRow, nTab ) ) )
				{
					if (bReference)
					{
						if (ppUsed[nArrX][nArrY])
							ppRefs[nArrX][nArrY].AddEntry( nCol, nRow, nTab );
						else
						{
							ppUsed[nArrX][nArrY] = sal_True;
							ppRefs[nArrX][nArrY].Init();
							ppRefs[nArrX][nArrY].AddEntry( nCol, nRow, nTab );
						}
					}
					else
					{
						double nVal;
						pSrcDoc->GetValue( nCol, nRow, nTab, nVal );
						if (ppUsed[nArrX][nArrY])
							lcl_UpdateArray( eFunction, ppCount[nArrX][nArrY],
										 ppSum[nArrX][nArrY], ppSumSqr[nArrX][nArrY],
										 nVal);
						else
						{
							ppUsed[nArrX][nArrY] = sal_True;
							lcl_InitArray( eFunction, ppCount[nArrX][nArrY],
												  ppSum[nArrX][nArrY],
												  ppSumSqr[nArrX][nArrY], nVal );
						}
					}
				}
			}
		}
	}

	delete[] pDestCols;
	delete[] pDestRows;
}

//	vorher testen, wieviele Zeilen eingefuegt werden (fuer Undo)

SCROW ScConsData::GetInsertCount() const
{
	SCROW nInsert = 0;
	SCSIZE nArrX;
	SCSIZE nArrY;
	if ( ppRefs && ppUsed )
	{
		for (nArrY=0; nArrY<nRowCount; nArrY++)
		{
			SCSIZE nNeeded = 0;
			for (nArrX=0; nArrX<nColCount; nArrX++)
				if (ppUsed[nArrX][nArrY])
					nNeeded = Max( nNeeded, ppRefs[nArrX][nArrY].GetCount() );

			nInsert += nNeeded;
		}
	}
	return nInsert;
}

//	fertige Daten ins Dokument schreiben
//!	optimieren nach Spalten?

void ScConsData::OutputToDocument( ScDocument* pDestDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
{
	OpCode eOpCode = eOpCodeTable[eFunction];

	SCSIZE nArrX;
	SCSIZE nArrY;

	//	Ecke links oben

	if ( bColByName && bRowByName && aCornerText.Len() )
		pDestDoc->SetString( nCol, nRow, nTab, aCornerText );

	//	Titel

	SCCOL nStartCol = nCol;
	SCROW nStartRow = nRow;
	if (bColByName)	++nStartRow;
	if (bRowByName)	++nStartCol;

	if (bColByName)
		for (SCSIZE i=0; i<nColCount; i++)
            pDestDoc->SetString( sal::static_int_cast<SCCOL>(nStartCol+i), nRow, nTab, *ppColHeaders[i] );
	if (bRowByName)
		for (SCSIZE j=0; j<nRowCount; j++)
            pDestDoc->SetString( nCol, sal::static_int_cast<SCROW>(nStartRow+j), nTab, *ppRowHeaders[j] );

	nCol = nStartCol;
	nRow = nStartRow;

	//	Daten

	if ( ppCount && ppUsed )							// Werte direkt einfuegen
	{
		for (nArrX=0; nArrX<nColCount; nArrX++)
			for (nArrY=0; nArrY<nRowCount; nArrY++)
				if (ppUsed[nArrX][nArrY])
				{
					double fVal = lcl_CalcData( eFunction, ppCount[nArrX][nArrY],
												ppSum[nArrX][nArrY],
												ppSumSqr[nArrX][nArrY]);
					if (ppCount[nArrX][nArrY] < 0.0)
                        pDestDoc->SetError( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                            sal::static_int_cast<SCROW>(nRow+nArrY), nTab, errNoValue );
					else
                        pDestDoc->SetValue( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                            sal::static_int_cast<SCROW>(nRow+nArrY), nTab, fVal );
				}
	}

	if ( ppRefs && ppUsed )								// Referenzen einfuegen
	{
								//! unterscheiden, ob nach Kategorien aufgeteilt
		String aString;

		ScSingleRefData aSRef;		// Daten fuer Referenz-Formelzellen
		aSRef.InitFlags();
		aSRef.SetFlag3D(sal_True);

		ScComplexRefData aCRef;			// Daten fuer Summen-Zellen
		aCRef.InitFlags();
		aCRef.Ref1.SetColRel(sal_True); aCRef.Ref1.SetRowRel(sal_True); aCRef.Ref1.SetTabRel(sal_True);
		aCRef.Ref2.SetColRel(sal_True); aCRef.Ref2.SetRowRel(sal_True); aCRef.Ref2.SetTabRel(sal_True);

		for (nArrY=0; nArrY<nRowCount; nArrY++)
		{
			SCSIZE nNeeded = 0;
			for (nArrX=0; nArrX<nColCount; nArrX++)
				if (ppUsed[nArrX][nArrY])
					nNeeded = Max( nNeeded, ppRefs[nArrX][nArrY].GetCount() );

			if (nNeeded)
			{
				pDestDoc->InsertRow( 0,nTab, MAXCOL,nTab, nRow+nArrY, nNeeded );

				for (nArrX=0; nArrX<nColCount; nArrX++)
					if (ppUsed[nArrX][nArrY])
					{
						ScReferenceList& rList = ppRefs[nArrX][nArrY];
						SCSIZE nCount = rList.GetCount();
						if (nCount)
						{
							for (SCSIZE nPos=0; nPos<nCount; nPos++)
							{
								ScReferenceEntry aRef = rList.GetEntry(nPos);
								if (aRef.nTab != SC_CONS_NOTFOUND)
								{
									//	Referenz einfuegen (absolut, 3d)

									aSRef.nCol = aRef.nCol;
									aSRef.nRow = aRef.nRow;
									aSRef.nTab = aRef.nTab;

									ScTokenArray aRefArr;
									aRefArr.AddSingleReference(aSRef);
									aRefArr.AddOpCode(ocStop);
                                    ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                                     sal::static_int_cast<SCROW>(nRow+nArrY+nPos), nTab );
									ScBaseCell* pCell = new ScFormulaCell( pDestDoc, aDest, &aRefArr );
									pDestDoc->PutCell( aDest.Col(), aDest.Row(), aDest.Tab(), pCell );
								}
							}

							//	Summe einfuegen (relativ, nicht 3d)

                            ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                             sal::static_int_cast<SCROW>(nRow+nArrY+nNeeded), nTab );

							aCRef.Ref1.nTab = aCRef.Ref2.nTab = nTab;
							aCRef.Ref1.nCol = aCRef.Ref2.nCol = sal::static_int_cast<SCsCOL>( nCol+nArrX );
							aCRef.Ref1.nRow = nRow+nArrY;
							aCRef.Ref2.nRow = nRow+nArrY+nNeeded-1;
							aCRef.CalcRelFromAbs( aDest );

							ScTokenArray aArr;
							aArr.AddOpCode(eOpCode);			// ausgewaehlte Funktion
							aArr.AddOpCode(ocOpen);
							aArr.AddDoubleReference(aCRef);
							aArr.AddOpCode(ocClose);
							aArr.AddOpCode(ocStop);
							ScBaseCell* pCell = new ScFormulaCell( pDestDoc, aDest, &aArr );
							pDestDoc->PutCell( aDest.Col(), aDest.Row(), aDest.Tab(), pCell );
						}
					}

				//	Gliederung einfuegen

				ScOutlineArray* pOutArr = pDestDoc->GetOutlineTable( nTab, sal_True )->GetRowArray();
				SCROW nOutStart = nRow+nArrY;
				SCROW nOutEnd = nRow+nArrY+nNeeded-1;
				sal_Bool bSize = sal_False;
				pOutArr->Insert( nOutStart, nOutEnd, bSize );
				for (SCROW nOutRow=nOutStart; nOutRow<=nOutEnd; nOutRow++)
					pDestDoc->ShowRow( nOutRow, nTab, sal_False );
				pDestDoc->UpdateOutlineRow( nOutStart, nOutEnd, nTab, sal_False );

				//	Zwischentitel

				if (ppTitlePos && ppTitles && ppRowHeaders)
				{
					String aDelim( RTL_CONSTASCII_USTRINGPARAM(" / ") );
					for (SCSIZE nPos=0; nPos<nDataCount; nPos++)
					{
						SCSIZE nTPos = ppTitlePos[nArrY][nPos];
						sal_Bool bDo = sal_True;
						if (nPos+1<nDataCount)
							if (ppTitlePos[nArrY][nPos+1] == nTPos)
								bDo = sal_False;									// leer
						if ( bDo && nTPos < nNeeded )
						{
							aString =  *ppRowHeaders[nArrY];
							aString += aDelim;
							aString += *ppTitles[nPos];
							pDestDoc->SetString( nCol-1, nRow+nArrY+nTPos, nTab, aString );
						}
					}
				}

				nRow += nNeeded;
			}
		}
	}
}





