/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */
 
#include <consoli.hxx>
#include <document.hxx>
#include <olinetab.hxx>
#include <subtotal.hxx>
#include <formula/errorcodes.hxx>
#include <formulacell.hxx>
#include <tokenarray.hxx>
#include <osl/diagnose.h>
#include <refdata.hxx>
 
#include <string.h>
#include <memory>
 
#define SC_CONS_NOTFOUND    -1
 
const OpCode eOpCodeTable[] = {      //  order as for enum ScSubTotalFunc
        ocBad,                              //  none
        ocAverage,
        ocCount,
        ocCount2,
        ocMax,
        ocMin,
        ocProduct,
        ocStDev,
        ocStDevP,
        ocSum,
        ocVar,
        ocVarP };
 
template< typename T >
static void lcl_AddString( ::std::vector<OUString>& rData, T& nCount, const OUString& rInsert )
{
    rData.push_back( rInsert);
    ++nCount;
}
 
ScConsData::ScConsData() :
    eFunction(SUBTOTAL_FUNC_SUM),
    bReference(false),
    bColByName(false),
    bRowByName(false),
    nColCount(0),
    nRowCount(0),
    nDataCount(0),
    bCornerUsed(false)
{
}
 
ScConsData::~ScConsData()
{
}
 
void ScConsData::DeleteData()
{
    ppRefs.reset();
    ppFunctionData.reset();
    ppUsed.reset();
    ppTitlePos.reset();
    ::std::vector<OUString>().swap( maColHeaders);
    ::std::vector<OUString>().swap( maRowHeaders);
    ::std::vector<OUString>().swap( maTitles);
    nDataCount = 0;
 
    if (bColByName) nColCount = 0;                  // otherwise maColHeaders is wrong
    if (bRowByName) nRowCount = 0;
 
    bCornerUsed = false;
    aCornerText.clear();
}
 
void ScConsData::InitData()
{
    if (bReference && nColCount && !ppRefs)
    {
        ppRefs.reset(new std::unique_ptr<ScReferenceList[]>[nColCount]);
        for (SCSIZE i=0; i<nColCount; i++)
            ppRefs[i].reset(new ScReferenceList[nRowCount]);
    }
    else if (nColCount && !ppFunctionData)
    {
        ppFunctionData.reset( new std::unique_ptr<ScFunctionData[]>[nColCount] );
        for (SCSIZE i=0; i<nColCount; i++)
        {
            ppFunctionData[i].reset( new ScFunctionData[nRowCount] );
        }
    }
 
    if (nColCount && !ppUsed)
    {
        ppUsed.reset( new std::unique_ptr<bool[]>[nColCount] );
        for (SCSIZE i=0; i<nColCount; i++)
        {
            ppUsed[i].reset( new bool[nRowCount] );
            memset( ppUsed[i].get(), 0, nRowCount * sizeof(bool) );
        }
    }
 
    if (nRowCount && nDataCount && !ppTitlePos)
    {
        ppTitlePos.reset( new std::unique_ptr<SCSIZE[]>[nRowCount] );
        for (SCSIZE i=0; i<nRowCount; i++)
        {
            ppTitlePos[i].reset( new SCSIZE[nDataCount] );
            memset( ppTitlePos[i].get(), 0, nDataCount * sizeof(SCSIZE) );    //TODO: not necessary ?
        }
    }
 
    //  CornerText: single String
}
 
void ScConsData::DoneFields()
{
    InitData();
}
 
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, bool bColName, bool bRowName, bool bRef )
{
    DeleteData();
    bReference = bRef;
    bColByName = bColName;
    if (bColName) nColCount = 0;
    bRowByName = bRowName;
    if (bRowName) nRowCount = 0;
    eFunction = eFunc;
}
 
void ScConsData::AddFields( const ScDocument* pSrcDoc, SCTAB nTab,
                            SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
    ++nDataCount;
 
    OUString aTitle;
 
    SCCOL nStartCol = nCol1;
    SCROW nStartRow = nRow1;
    if (bColByName) ++nStartRow;
    if (bRowByName) ++nStartCol;
 
    if (bColByName)
    {
        for (SCCOL nCol=nStartCol; nCol<=nCol2; nCol++)
        {
            aTitle = pSrcDoc->GetString(nCol, nRow1, nTab);
            if (!aTitle.isEmpty())
            {
                bool bFound = false;
                for (SCSIZE i=0; i<nColCount && !bFound; i++)
                    if ( maColHeaders[i] == aTitle )
                        bFound = true;
                if (!bFound)
                    lcl_AddString( maColHeaders, nColCount, aTitle );
            }
        }
    }
 
    if (!bRowByName)
        return;
 
    for (SCROW nRow=nStartRow; nRow<=nRow2; nRow++)
    {
        aTitle = pSrcDoc->GetString(nCol1, nRow, nTab);
        if (!aTitle.isEmpty())
        {
            bool bFound = false;
            for (SCSIZE i=0; i<nRowCount && !bFound; i++)
                if ( maRowHeaders[i] == aTitle )
                    bFound = true;
            if (!bFound)
                lcl_AddString( maRowHeaders, nRowCount, aTitle );
        }
    }
}
 
void ScConsData::AddName( const OUString& rName )
{
    SCSIZE nArrX;
    SCSIZE nArrY;
 
    if (!bReference)
        return;
 
    maTitles.push_back( rName);
    size_t nTitleCount = maTitles.size();
 
    for (nArrY=0; nArrY<nRowCount; nArrY++)
    {
        //  set all data to same length
 
        SCSIZE nMax = 0;
        for (nArrX=0; nArrX<nColCount; nArrX++)
            nMax = std::max( nMax, ppRefs[nArrX][nArrY].size() );
 
        for (nArrX=0; nArrX<nColCount; nArrX++)
        {
            ppUsed[nArrX][nArrY] = true;
            ppRefs[nArrX][nArrY].resize( nMax, { SC_CONS_NOTFOUND, SC_CONS_NOTFOUND, SC_CONS_NOTFOUND });
        }
 
        //  store positions
 
        if (ppTitlePos)
            if (nTitleCount < nDataCount)
                ppTitlePos[nArrY][nTitleCount] = nMax;
    }
}
 
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 )
    {
        OSL_FAIL("range too big");
        nCol2 = sal::static_int_cast<SCCOL>( nCol1 + nColCount - 1 );
    }
    if ( nRow2 >= sal::static_int_cast<SCROW>(nRow1 + nRowCount) && !bRowByName )
    {
        OSL_FAIL("range too big");
        nRow2 = sal::static_int_cast<SCROW>( nRow1 + nRowCount - 1 );
    }
 
    SCCOL nCol;
    SCROW nRow;
 
    // left top corner
 
    if ( bColByName && bRowByName )
    {
        OUString aThisCorner = pSrcDoc->GetString(nCol1, nRow1, nTab);
        if (bCornerUsed)
        {
            if (aCornerText != aThisCorner)
                aCornerText.clear();
        }
        else
        {
            aCornerText = aThisCorner;
            bCornerUsed = true;
        }
    }
 
    // search title
 
    SCCOL nStartCol = nCol1;
    SCROW nStartRow = nRow1;
    if (bColByName) ++nStartRow;
    if (bRowByName) ++nStartCol;
    OUString aTitle;
    std::unique_ptr<SCCOL[]> pDestCols;
    std::unique_ptr<SCROW[]> pDestRows;
    if (bColByName)
    {
        pDestCols.reset(new SCCOL[nCol2-nStartCol+1]);
        for (nCol=nStartCol; nCol<=nCol2; nCol++)
        {
            aTitle = pSrcDoc->GetString(nCol, nRow1, nTab);
            SCCOL nPos = SC_CONS_NOTFOUND;
            if (!aTitle.isEmpty())
            {
                bool bFound = false;
                for (SCSIZE i=0; i<nColCount && !bFound; i++)
                    if ( maColHeaders[i] == aTitle )
                    {
                        nPos = static_cast<SCCOL>(i);
                        bFound = true;
                    }
                OSL_ENSURE(bFound, "column not found");
            }
            pDestCols[nCol-nStartCol] = nPos;
        }
    }
    if (bRowByName)
    {
        pDestRows.reset(new SCROW[nRow2-nStartRow+1]);
        for (nRow=nStartRow; nRow<=nRow2; nRow++)
        {
            aTitle = pSrcDoc->GetString(nCol1, nRow, nTab);
            SCROW nPos = SC_CONS_NOTFOUND;
            if (!aTitle.isEmpty())
            {
                bool bFound = false;
                for (SCSIZE i=0; i<nRowCount && !bFound; i++)
                    if ( maRowHeaders[i] == aTitle )
                    {
                        nPos = static_cast<SCROW>(i);
                        bFound = true;
                    }
                OSL_ENSURE(bFound, "row not found");
            }
            pDestRows[nRow-nStartRow] = nPos;
        }
    }
    nCol1 = nStartCol;
    nRow1 = nStartRow;
 
    // data
 
    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)
                    {
                        ppUsed[nArrX][nArrY] = true;
                        ppRefs[nArrX][nArrY].push_back( { nCol, nRow, nTab } );
                    }
                    else
                    {
                        double nVal = pSrcDoc->GetValue( nCol, nRow, nTab );
                        if (!ppUsed[nArrX][nArrY])
                        {
                            ppUsed[nArrX][nArrY] = true;
                            ppFunctionData[nArrX][nArrY] = ScFunctionData( eFunction);
                        }
                        ppFunctionData[nArrX][nArrY].update( nVal);
                    }
                }
            }
        }
    }
}
 
// check before, how many rows to insert (for 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++)
                nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() );
 
            nInsert += nNeeded;
        }
    }
    return nInsert;
}
 
// store completed data to document
//TODO: optimize on columns?
 
void ScConsData::OutputToDocument( ScDocument& rDestDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
{
    OpCode eOpCode = eOpCodeTable[eFunction];
 
    SCSIZE nArrX;
    SCSIZE nArrY;
 
    // left top corner
 
    if ( bColByName && bRowByName && !aCornerText.isEmpty() )
        rDestDoc.SetString( nCol, nRow, nTab, aCornerText );
 
    // title
 
    SCCOL nStartCol = nCol;
    SCROW nStartRow = nRow;
    if (bColByName) ++nStartRow;
    if (bRowByName) ++nStartCol;
 
    if (bColByName)
        for (SCSIZE i=0; i<nColCount; i++)
            rDestDoc.SetString( sal::static_int_cast<SCCOL>(nStartCol+i), nRow, nTab, maColHeaders[i] );
    if (bRowByName)
        for (SCSIZE j=0; j<nRowCount; j++)
            rDestDoc.SetString( nCol, sal::static_int_cast<SCROW>(nStartRow+j), nTab, maRowHeaders[j] );
 
    nCol = nStartCol;
    nRow = nStartRow;
 
    // data
 
    if ( ppFunctionData && ppUsed )    // insert values directly
    {
        for (nArrX=0; nArrX<nColCount; nArrX++)
            for (nArrY=0; nArrY<nRowCount; nArrY++)
                if (ppUsed[nArrX][nArrY])
                {
                    double fVal = ppFunctionData[nArrX][nArrY].getResult();
                    if (ppFunctionData[nArrX][nArrY].getError())
                        rDestDoc.SetError( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                           sal::static_int_cast<SCROW>(nRow+nArrY), nTab, FormulaError::NoValue );
                    else
                        rDestDoc.SetValue( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                           sal::static_int_cast<SCROW>(nRow+nArrY), nTab, fVal );
                }
    }
 
    if ( !(ppRefs && ppUsed) )     // insert Reference
                                return;
 
                            //TODO: differentiate, if split into categories
    OUString aString;
 
    ScSingleRefData aSRef;  // data for Reference formula cells
    aSRef.InitFlags();      // this reference is absolute at all times
    aSRef.SetFlag3D(true);
 
    ScComplexRefData aCRef; // data for Sum cells
    aCRef.InitFlags();
    aCRef.Ref1.SetColRel(true); aCRef.Ref1.SetRowRel(true); aCRef.Ref1.SetTabRel(true);
    aCRef.Ref2.SetColRel(true); aCRef.Ref2.SetRowRel(true); aCRef.Ref2.SetTabRel(true);
 
    for (nArrY=0; nArrY<nRowCount; nArrY++)
    {
        SCSIZE nNeeded = 0;
        for (nArrX=0; nArrX<nColCount; nArrX++)
            nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() );
 
        if (nNeeded)
        {
            rDestDoc.InsertRow( 0,nTab, rDestDoc.MaxCol(),nTab, nRow+nArrY, nNeeded );
 
            for (nArrX=0; nArrX<nColCount; nArrX++)
                if (ppUsed[nArrX][nArrY])
                {
                    SCSIZE nCount = ppRefs[nArrX][nArrY].size();
                    if (nCount)
                    {
                        for (SCSIZE nPos=0; nPos<nCount; nPos++)
                        {
                            ScReferenceEntry aRef = ppRefs[nArrX][nArrY][nPos];
                            if (aRef.nTab != SC_CONS_NOTFOUND)
                            {
                                // insert reference (absolute, 3d)
 
                                aSRef.SetAddress(rDestDoc.GetSheetLimits(), ScAddress(aRef.nCol,aRef.nRow,aRef.nTab), ScAddress());
 
                                ScTokenArray aRefArr(rDestDoc);
                                aRefArr.AddSingleReference(aSRef);
                                aRefArr.AddOpCode(ocStop);
                                ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                                 sal::static_int_cast<SCROW>(nRow+nArrY+nPos), nTab );
                                ScFormulaCell* pCell = new ScFormulaCell(rDestDoc, aDest, aRefArr);
                                rDestDoc.SetFormulaCell(aDest, pCell);
                            }
                        }
 
                        // insert sum (relative, not 3d)
 
                        ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX),
                                         sal::static_int_cast<SCROW>(nRow+nArrY+nNeeded), nTab );
 
                        ScRange aRange(sal::static_int_cast<SCCOL>(nCol+nArrX), nRow+nArrY, nTab);
                        aRange.aEnd.SetRow(nRow+nArrY+nNeeded-1);
                        aCRef.SetRange(rDestDoc.GetSheetLimits(), aRange, aDest);
 
                        ScTokenArray aArr(rDestDoc);
                        aArr.AddOpCode(eOpCode);            // selected function
                        aArr.AddOpCode(ocOpen);
                        aArr.AddDoubleReference(aCRef);
                        aArr.AddOpCode(ocClose);
                        aArr.AddOpCode(ocStop);
                        ScFormulaCell* pCell = new ScFormulaCell(rDestDoc, aDest, aArr);
                        rDestDoc.SetFormulaCell(aDest, pCell);
                    }
                }
 
            // insert outline
 
            ScOutlineArray& rOutArr = rDestDoc.GetOutlineTable( nTab, true )->GetRowArray();
            SCROW nOutStart = nRow+nArrY;
            SCROW nOutEnd = nRow+nArrY+nNeeded-1;
            bool bSize = false;
            rOutArr.Insert( nOutStart, nOutEnd, bSize );
            for (SCROW nOutRow=nOutStart; nOutRow<=nOutEnd; nOutRow++)
                rDestDoc.ShowRow( nOutRow, nTab, false );
            rDestDoc.SetDrawPageSize(nTab);
            rDestDoc.UpdateOutlineRow( nOutStart, nOutEnd, nTab, false );
 
            // sub title
 
            if (ppTitlePos && !maTitles.empty() && !maRowHeaders.empty())
            {
                for (SCSIZE nPos=0; nPos<nDataCount; nPos++)
                {
                    SCSIZE nTPos = ppTitlePos[nArrY][nPos];
                    bool bDo = true;
                    if (nPos+1<nDataCount)
                        if (ppTitlePos[nArrY][nPos+1] == nTPos)
                            bDo = false;                                    // empty
                    if ( bDo && nTPos < nNeeded )
                    {
                        aString = maRowHeaders[nArrY] + " / " + maTitles[nPos];
                        rDestDoc.SetString( nCol-1, nRow+nArrY+nTPos, nTab, aString );
                    }
                }
            }
 
            nRow += nNeeded;
        }
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V522 Dereferencing of the null pointer 'ppRefs' might take place.

V522 Dereferencing of the null pointer 'ppFunctionData' might take place.

V522 Dereferencing of the null pointer 'ppUsed' might take place.

V522 Dereferencing of the null pointer 'ppTitlePos' might take place.