/**************************************************************
 * 
 * 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 "scitems.hxx"
#include <svl/intitem.hxx>
#include <svl/zforlist.hxx>
#include <float.h>				// DBL_MIN

#include "chartarr.hxx"
#include "document.hxx"
#include "rechead.hxx"
#include "globstr.hrc"
#include "cell.hxx"
#include "docoptio.hxx"

#include <vector>

using ::std::vector;

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

ScMemChart::ScMemChart(short nCols, short nRows)
{
    nRowCnt = nRows;
    nColCnt = nCols;
    pData   = new double[nColCnt * nRowCnt];

    if (pData)
    {
        double *pFill = pData;

        for (short i = 0; i < nColCnt; i++)
            for (short j = 0; j < nRowCnt; j++)
                *(pFill ++) = 0.0;
    }

    pColText = new String[nColCnt];
    pRowText = new String[nRowCnt];
}

ScMemChart::~ScMemChart()
{
    delete[] pRowText;
    delete[] pColText;
    delete[] pData;
}

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

ScChartArray::ScChartArray( ScDocument* pDoc, SCTAB nTab,
                    SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP,
					const String& rChartName ) :
		aName( rChartName ),
		pDocument( pDoc ),
        aPositioner(pDoc, nTab, nStartColP, nStartRowP, nEndColP, nEndRowP),
		bValid( sal_True )
{
}

ScChartArray::ScChartArray( ScDocument* pDoc, const ScRangeListRef& rRangeList,
					const String& rChartName ) :
		aName( rChartName ),
		pDocument( pDoc ),
        aPositioner(pDoc, rRangeList),
		bValid( sal_True )
{
}

ScChartArray::ScChartArray( const ScChartArray& rArr ) :
        ScDataObject(),
		aName(rArr.aName),
		pDocument(rArr.pDocument),
        aPositioner(rArr.aPositioner),
		bValid(rArr.bValid)
{
}

ScChartArray::~ScChartArray()
{
}

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

sal_Bool ScChartArray::operator==(const ScChartArray& rCmp) const
{
	return aPositioner == rCmp.aPositioner
		&& aName == rCmp.aName;
}

#ifdef _MSC_VER
#pragma optimize("",off)
#endif

ScMemChart* ScChartArray::CreateMemChart()
{
    ScRangeListRef aRangeListRef(GetRangeList());
	sal_uLong nCount = aRangeListRef->Count();
	if ( nCount > 1 )
		return CreateMemChartMulti();
	else if ( nCount == 1 )
	{
		ScRange* pR = aRangeListRef->First();
		if ( pR->aStart.Tab() != pR->aEnd.Tab() )
			return CreateMemChartMulti();
		else
			return CreateMemChartSingle();
	}
	else
		return CreateMemChartMulti();	// kann 0 Range besser ab als Single
}

ScMemChart* ScChartArray::CreateMemChartSingle()
{
	SCSIZE nCol;
	SCSIZE nRow;

		//
		//	wirkliche Groesse (ohne versteckte Zeilen/Spalten)
		//

	SCCOL nColAdd = HasRowHeaders() ? 1 : 0;
	SCROW nRowAdd = HasColHeaders() ? 1 : 0;

	SCCOL nCol1;
	SCROW nRow1;
	SCTAB nTab1;
	SCCOL nCol2;
	SCROW nRow2;
	SCTAB nTab2;
	ScRangeListRef aRangeListRef(GetRangeList());
	aRangeListRef->First()->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );

	SCCOL nStrCol = nCol1;		// fuer Beschriftung merken
	SCROW nStrRow = nRow1;
	// Skip hidden columns.
	// TODO: make use of last column value once implemented.
	SCCOL nLastCol = -1;
	while (pDocument->ColHidden(nCol1, nTab1, nLastCol))
		++nCol1;

	// Skip hidden rows.
	SCROW nLastRow = -1;
	if (pDocument->RowHidden(nRow1, nTab1, nLastRow))
		nRow1 = nLastRow + 1;

	// falls alles hidden ist, bleibt die Beschriftung am Anfang
	if ( nCol1 <= nCol2 )
	{
		nStrCol = nCol1;
        nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nColAdd );
	}
	if ( nRow1 <= nRow2 )
	{
		nStrRow = nRow1;
        nRow1 = sal::static_int_cast<SCROW>( nRow1 + nRowAdd );
	}

	SCSIZE nTotalCols = ( nCol1 <= nCol2 ? nCol2 - nCol1 + 1 : 0 );
	vector<SCCOL> aCols;
	aCols.reserve(nTotalCols);
	for (SCSIZE i=0; i<nTotalCols; i++)
	{
		SCCOL nThisCol = sal::static_int_cast<SCCOL>(nCol1+i);
		if (!pDocument->ColHidden(nThisCol, nTab1, nLastCol))
			aCols.push_back(nThisCol);
	}
	SCSIZE nColCount = aCols.size();

	SCSIZE nTotalRows = ( nRow1 <= nRow2 ? nRow2 - nRow1 + 1 : 0 );
	vector<SCROW> aRows;
	aRows.reserve(nTotalRows);
	if (nRow1 <= nRow2)
	{
		// Get all visible rows between nRow1 and nRow2.
		SCROW nThisRow = nRow1;
		while (nThisRow <= nRow2)
		{
			if (pDocument->RowHidden(nThisRow, nTab1, nLastRow))
				nThisRow = nLastRow;
			else
				aRows.push_back(nThisRow);
			++nThisRow;
		}
	}
	SCSIZE nRowCount = aRows.size();

    // May happen at least with more than 32k rows.
    if (nColCount > SHRT_MAX || nRowCount > SHRT_MAX)
    {
        nColCount = 0;
        nRowCount = 0;
    }

	sal_Bool bValidData = sal_True;
	if ( !nColCount )
	{
		bValidData = sal_False;
		nColCount = 1;
		aCols.push_back(nStrCol);
	}
	if ( !nRowCount )
	{
		bValidData = sal_False;
		nRowCount = 1;
		aRows.push_back(nStrRow);
	}

		//
		//	Daten
		//

    ScMemChart* pMemChart = new ScMemChart(
            static_cast<short>(nColCount), static_cast<short>(nRowCount) );
	if (pMemChart)
	{
// 		SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
//		pMemChart->SetNumberFormatter( pFormatter );
		if ( bValidData )
		{
			sal_Bool bCalcAsShown = pDocument->GetDocOptions().IsCalcAsShown();
			ScBaseCell* pCell;
			for (nCol=0; nCol<nColCount; nCol++)
			{
				for (nRow=0; nRow<nRowCount; nRow++)
				{
					double nVal = DBL_MIN;		// Hack fuer Chart, um leere Zellen zu erkennen

					pDocument->GetCell( aCols[nCol], aRows[nRow], nTab1, pCell );
					if (pCell)
					{
						CellType eType = pCell->GetCellType();
						if (eType == CELLTYPE_VALUE)
						{
							nVal = ((ScValueCell*)pCell)->GetValue();
							if ( bCalcAsShown && nVal != 0.0 )
							{
								sal_uInt32 nFormat;
								pDocument->GetNumberFormat( aCols[nCol],
									aRows[nRow], nTab1, nFormat );
								nVal = pDocument->RoundValueAsShown( nVal, nFormat );
							}
						}
						else if (eType == CELLTYPE_FORMULA)
						{
							ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
							if ( (pFCell->GetErrCode() == 0) && pFCell->IsValue() )
								nVal = pFCell->GetValue();
						}
					}
					pMemChart->SetData(static_cast<short>(nCol), static_cast<short>(nRow), nVal);
				}
			}
		}
		else
		{
			//!	Flag, dass Daten ungueltig ??

			for (nCol=0; nCol<nColCount; nCol++)
				for (nRow=0; nRow<nRowCount; nRow++)
					pMemChart->SetData( static_cast<short>(nCol), static_cast<short>(nRow), DBL_MIN );
		}

		//
		//	Spalten-Header
		//

		for (nCol=0; nCol<nColCount; nCol++)
		{
            String aString, aColStr;
			if (HasColHeaders())
				pDocument->GetString( aCols[nCol], nStrRow, nTab1, aString );
			if ( !aString.Len() )
			{
				aString = ScGlobal::GetRscString(STR_COLUMN);
				aString += ' ';
//                aString += String::CreateFromInt32( pCols[nCol]+1 );
                ScAddress aPos( aCols[ nCol ], 0, 0 );
                aPos.Format( aColStr, SCA_VALID_COL, NULL );
                aString += aColStr;
			}
			pMemChart->SetColText( static_cast<short>(nCol), aString);

//            sal_uLong nNumberAttr = (nTotalRows ? pDocument->GetNumberFormat(
//                        ScAddress( pCols[nCol], nRow1, nTab1)) : 0);
//			pMemChart->SetNumFormatIdCol( static_cast<long>(nCol), nNumberAttr );
		}

		//
		//	Zeilen-Header
		//

		for (nRow=0; nRow<nRowCount; nRow++)
		{
			String aString;
			if (HasRowHeaders())
			{
				ScAddress aAddr( nStrCol, aRows[nRow], nTab1 );
				pDocument->GetString( nStrCol, aRows[nRow], nTab1, aString );
			}
			if ( !aString.Len() )
			{
				aString = ScGlobal::GetRscString(STR_ROW);
				aString += ' ';
				aString += String::CreateFromInt32( aRows[nRow]+1 );
			}
			pMemChart->SetRowText( static_cast<short>(nRow), aString);

//            sal_uLong nNumberAttr = (nTotalCols ? pDocument->GetNumberFormat(
//                        ScAddress( nCol1, pRows[nRow], nTab1)) : 0);
//			pMemChart->SetNumFormatIdRow( static_cast<long>(nRow), nNumberAttr );
		}

		//
		//  Titel
		//

//		pMemChart->SetMainTitle(ScGlobal::GetRscString(STR_CHART_MAINTITLE));
//		pMemChart->SetSubTitle(ScGlobal::GetRscString(STR_CHART_SUBTITLE));
//		pMemChart->SetXAxisTitle(ScGlobal::GetRscString(STR_CHART_XTITLE));
//		pMemChart->SetYAxisTitle(ScGlobal::GetRscString(STR_CHART_YTITLE));
//		pMemChart->SetZAxisTitle(ScGlobal::GetRscString(STR_CHART_ZTITLE));

		//
		//	Zahlen-Typ
		//

//        sal_uLong nNumberAttr = (nTotalCols && nTotalRows ?
//                pDocument->GetNumberFormat( ScAddress( nCol1, nRow1, nTab1)) :
//                0);
//		if (pFormatter)
//			pMemChart->SetDataType(pFormatter->GetType( nNumberAttr ));

		//
		//	Parameter-Strings
		//

//        SetExtraStrings( *pMemChart );
	}

	return pMemChart;
}

ScMemChart* ScChartArray::CreateMemChartMulti()
{
	SCSIZE nColCount = GetPositionMap()->GetColCount();
	SCSIZE nRowCount = GetPositionMap()->GetRowCount();

	SCSIZE nCol = 0;
	SCSIZE nRow = 0;

    // May happen at least with more than 32k rows.
    if (nColCount > SHRT_MAX || nRowCount > SHRT_MAX)
    {
        nColCount = 0;
        nRowCount = 0;
    }

	sal_Bool bValidData = sal_True;
	if ( !nColCount )
	{
		bValidData = sal_False;
		nColCount = 1;
	}
	if ( !nRowCount )
	{
		bValidData = sal_False;
		nRowCount = 1;
	}

	//
	//	Daten
	//

    ScMemChart* pMemChart = new ScMemChart(
            static_cast<short>(nColCount), static_cast<short>(nRowCount) );
	if (pMemChart)
	{
//		pMemChart->SetNumberFormatter( pDocument->GetFormatTable() );
        sal_Bool bCalcAsShown = pDocument->GetDocOptions().IsCalcAsShown();
        sal_uLong nIndex = 0;
        if (bValidData)
        {
            for ( nCol = 0; nCol < nColCount; nCol++ )
            {
                for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ )
                {
                    double nVal = DBL_MIN;		// Hack fuer Chart, um leere Zellen zu erkennen
                    const ScAddress* pPos = GetPositionMap()->GetPosition( nIndex );
                    if ( pPos )
                    {	// sonst: Luecke
                        ScBaseCell* pCell = pDocument->GetCell( *pPos );
                        if (pCell)
                        {
                            CellType eType = pCell->GetCellType();
                            if (eType == CELLTYPE_VALUE)
                            {
                                nVal = ((ScValueCell*)pCell)->GetValue();
                                if ( bCalcAsShown && nVal != 0.0 )
                                {
                                    sal_uLong nFormat = pDocument->GetNumberFormat( *pPos );
                                    nVal = pDocument->RoundValueAsShown( nVal, nFormat );
                                }
                            }
                            else if (eType == CELLTYPE_FORMULA)
                            {
                                ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
                                if ( (pFCell->GetErrCode() == 0) && pFCell->IsValue() )
                                    nVal = pFCell->GetValue();
                            }
                        }
                    }
                    pMemChart->SetData(static_cast<short>(nCol), static_cast<short>(nRow), nVal);
                }
            }
        }
		else
		{
			for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ )
			{
				double nVal = DBL_MIN;		// Hack fuer Chart, um leere Zellen zu erkennen
				const ScAddress* pPos = GetPositionMap()->GetPosition( nIndex );
				if ( pPos )
				{	// sonst: Luecke
					ScBaseCell* pCell = pDocument->GetCell( *pPos );
					if (pCell)
					{
						CellType eType = pCell->GetCellType();
						if (eType == CELLTYPE_VALUE)
						{
							nVal = ((ScValueCell*)pCell)->GetValue();
							if ( bCalcAsShown && nVal != 0.0 )
							{
								sal_uLong nFormat = pDocument->GetNumberFormat( *pPos );
								nVal = pDocument->RoundValueAsShown( nVal, nFormat );
							}
						}
						else if (eType == CELLTYPE_FORMULA)
						{
							ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
							if ( (pFCell->GetErrCode() == 0) && pFCell->IsValue() )
								nVal = pFCell->GetValue();
						}
					}
				}
				pMemChart->SetData(static_cast<short>(nCol), static_cast<short>(nRow), nVal);
			}
		}

//2do: Beschriftung bei Luecken

		//
		//	Spalten-Header
		//

		SCCOL nPosCol = 0;
		for ( nCol = 0; nCol < nColCount; nCol++ )
		{
            String aString, aColStr;
			const ScAddress* pPos = GetPositionMap()->GetColHeaderPosition( static_cast<SCCOL>(nCol) );
			if ( HasColHeaders() && pPos )
				pDocument->GetString(
					pPos->Col(), pPos->Row(), pPos->Tab(), aString );
			if ( !aString.Len() )
			{
				aString = ScGlobal::GetRscString(STR_COLUMN);
				aString += ' ';
				if ( pPos )
					nPosCol = pPos->Col() + 1;
				else
					nPosCol++;
                ScAddress aPos( nPosCol - 1, 0, 0 );
                aPos.Format( aColStr, SCA_VALID_COL, NULL );
//                aString += String::CreateFromInt32( nPosCol );
                aString += aColStr;
			}
			pMemChart->SetColText( static_cast<short>(nCol), aString);

//			sal_uLong nNumberAttr = 0;
//			pPos = GetPositionMap()->GetPosition( nCol, 0 );
//			if ( pPos )
//				nNumberAttr = pDocument->GetNumberFormat( *pPos );
//			pMemChart->SetNumFormatIdCol( static_cast<long>(nCol), nNumberAttr );
		}

		//
		//	Zeilen-Header
		//

		SCROW nPosRow = 0;
		for ( nRow = 0; nRow < nRowCount; nRow++ )
		{
			String aString;
			const ScAddress* pPos = GetPositionMap()->GetRowHeaderPosition( nRow );
			if ( HasRowHeaders() && pPos )
			{
				pDocument->GetString(
					pPos->Col(), pPos->Row(), pPos->Tab(), aString );
			}
			if ( !aString.Len() )
			{
				aString = ScGlobal::GetRscString(STR_ROW);
				aString += ' ';
				if ( pPos )
					nPosRow = pPos->Row() + 1;
				else
					nPosRow++;
				aString += String::CreateFromInt32( nPosRow );
			}
			pMemChart->SetRowText( static_cast<short>(nRow), aString);

//			sal_uLong nNumberAttr = 0;
//			pPos = GetPositionMap()->GetPosition( 0, nRow );
//			if ( pPos )
//				nNumberAttr = pDocument->GetNumberFormat( *pPos );
//			pMemChart->SetNumFormatIdRow( static_cast<long>(nRow), nNumberAttr );
		}

		//
		//  Titel
		//

//		pMemChart->SetMainTitle(ScGlobal::GetRscString(STR_CHART_MAINTITLE));
//		pMemChart->SetSubTitle(ScGlobal::GetRscString(STR_CHART_SUBTITLE));
//		pMemChart->SetXAxisTitle(ScGlobal::GetRscString(STR_CHART_XTITLE));
//		pMemChart->SetYAxisTitle(ScGlobal::GetRscString(STR_CHART_YTITLE));
//		pMemChart->SetZAxisTitle(ScGlobal::GetRscString(STR_CHART_ZTITLE));

		//
		//	Zahlen-Typ
		//

//		SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
//		if (pFormatter)
//		{
//			sal_uLong nIndex = 0;
//			sal_uLong nCount = GetPositionMap()->GetCount();
//			const ScAddress* pPos;
//			do
//			{
//				pPos = GetPositionMap()->GetPosition( nIndex );
//			} while ( !pPos && ++nIndex < nCount );
//			sal_uLong nFormat = ( pPos ? pDocument->GetNumberFormat( *pPos ) : 0 );
//			pMemChart->SetDataType( pFormatter->GetType( nFormat ) );
//		}

		//
		//	Parameter-Strings
		//

//        SetExtraStrings( *pMemChart );
	}

	return pMemChart;
}

#ifdef _MSC_VER
#pragma optimize("",on)
#endif


//
//				Collection
//

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

sal_Bool ScChartCollection::operator==(const ScChartCollection& rCmp) const
{
	if (nCount != rCmp.nCount)
		return sal_False;

	for (sal_uInt16 i=0; i<nCount; i++)
		if (!((*(const ScChartArray*)pItems[i]) == (*(const ScChartArray*)rCmp.pItems[i])))
			return sal_False;

	return sal_True;
}

