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



#ifndef SC_JUMPMATRIX_HXX
#define SC_JUMPMATRIX_HXX

#include "formula/token.hxx"
#include "formula/errorcodes.hxx"
#include <tools/solar.h>
#include <vector>
#include "scmatrix.hxx"

typedef ::std::vector< formula::FormulaToken*> ScTokenVec;

struct ScJumpMatrixEntry
{
    double  fBool;      // 0:= false  1:= true   also if no-path
                        // other values may contain error conditions like NAN and INF
    short   nStart;     // start of path (actually start-1, see formula::FormulaTokenIterator)
    short   nNext;      // next after path
                        // jump path exists if nStart != nNext, else no path
    short   nStop;      // optional stop of path (nPC < nStop)

    void    SetJump( double fBoolP, short nStartP, short nNextP, short nStopP )
                {
                    fBool = fBoolP;
                    nStart = nStartP;
                    nNext = nNextP;
                    nStop = nStopP;
                }
    void    GetJump( double& rBool, short& rStart, short& rNext, short& rStop )
                {
                    rBool = fBool;
                    rStart = nStart;
                    rNext = nNext;
                    rStop = nStop;
                }
};

class ScJumpMatrix
{
            ScJumpMatrixEntry*  pJump;      // the jumps
            ScMatrixRef         pMat;       // the results
            ScTokenVec*         pParams;    // parameter stack
            SCSIZE              nCols;
            SCSIZE              nRows;
            SCSIZE              nCurCol;
            SCSIZE              nCurRow;
            SCSIZE              nResMatCols;
            SCSIZE              nResMatRows;
            bool                bStarted;

                                // not implemented, prevent usage
                                ScJumpMatrix( const ScJumpMatrix& );
            ScJumpMatrix&       operator=( const ScJumpMatrix& );

public:
                                ScJumpMatrix( SCSIZE nColsP, SCSIZE nRowsP )
                                        : pJump( new ScJumpMatrixEntry[ nColsP * nRowsP ] )
                                        , pMat( new ScMatrix( nColsP, nRowsP) )
                                        , pParams( NULL )
                                        , nCols( nColsP )
                                        , nRows( nRowsP )
                                        , nCurCol( 0 )
                                        , nCurRow( 0 )
                                        , nResMatCols( nColsP )
                                        , nResMatRows( nRowsP )
                                        , bStarted( false )
                                    {
                                        // Initialize result matrix in case of
                                        // a premature end of the interpreter
                                        // due to errors.
                                        pMat->FillDouble( CreateDoubleError(
                                                NOTAVAILABLE), 0, 0, nCols-1,
                                                nRows-1);
                                        /*! pJump not initialized */
                                    }
                                ~ScJumpMatrix()
                                    {
                                        if ( pParams )
                                        {
                                            for ( ScTokenVec::iterator i =
                                                    pParams->begin(); i !=
                                                    pParams->end(); ++i )
                                            {
                                                (*i)->DecRef();
                                            }
                                            delete pParams;
                                        }
                                        delete [] pJump;
                                    }
            void                GetDimensions( SCSIZE& rCols, SCSIZE& rRows ) const
                                    {
                                        rCols = nCols;
                                        rRows = nRows;
                                    }
            void                SetJump( SCSIZE nCol, SCSIZE nRow, double fBool,
                                            short nStart, short nNext,
                                            short nStop = SHRT_MAX )
                                    {
                                        pJump[ (sal_uLong)nCol * nRows + nRow ].
                                            SetJump( fBool, nStart, nNext, nStop);
                                    }
            void                GetJump( SCSIZE nCol, SCSIZE nRow, double& rBool,
                                            short& rStart, short& rNext,
                                            short& rStop ) const
                                    {
                                        if (nCols == 1 && nRows == 1)
                                        {
                                            nCol = 0;
                                            nRow = 0;
                                        }
                                        else if (nCols == 1 && nRow < nRows)
                                            nCol = 0;
                                        else if (nRows == 1 && nCol < nCols)
                                            nRow = 0;
                                        else if (nCols <= nCol || nRows <= nRow)
                                        {
                                            DBG_ERROR("ScJumpMatrix::GetJump: dimension error");
                                            nCol = 0;
                                            nRow = 0;
                                        }
                                        pJump[ (sal_uLong)nCol * nRows + nRow ].
                                            GetJump( rBool, rStart, rNext, rStop);
                                    }
            void                SetAllJumps( double fBool,
                                            short nStart, short nNext,
                                            short nStop = SHRT_MAX )
                                    {
                                        sal_uLong n = (sal_uLong)nCols * nRows;
                                        for ( sal_uLong j=0; j<n; ++j )
                                        {
                                            pJump[ j ].SetJump( fBool, nStart,
                                                    nNext, nStop);
                                        }
                                    }
            void                SetJumpParameters( ScTokenVec* p )
                                    { pParams = p; }
            const ScTokenVec*   GetJumpParameters() const { return pParams; }
            ScMatrix*           GetResultMatrix() const { return pMat; }
            void                GetPos( SCSIZE& rCol, SCSIZE& rRow ) const
                                    {
                                        rCol = nCurCol;
                                        rRow = nCurRow;
                                    }
            bool                Next( SCSIZE& rCol, SCSIZE& rRow )
                                    {
                                        if ( !bStarted )
                                        {
                                            bStarted = true;
                                            nCurCol = nCurRow = 0;
                                        }
                                        else
                                        {
                                            if ( ++nCurRow >= nResMatRows )
                                            {
                                                nCurRow = 0;
                                                ++nCurCol;
                                            }
                                        }
                                        GetPos( rCol, rRow );
                                        return nCurCol < nResMatCols;
                                    }
            void                GetResMatDimensions( SCSIZE& rCols, SCSIZE& rRows )
                                    {
                                        rCols = nResMatCols;
                                        rRows = nResMatRows;
                                    }
            void                SetNewResMat( SCSIZE nNewCols, SCSIZE nNewRows )
                                    {
                                        if ( nNewCols > nResMatCols || nNewRows > nResMatRows )
                                        {
                                            pMat = pMat->CloneAndExtend( nNewCols, nNewRows );
                                            if ( nResMatCols < nNewCols )
                                            {
                                                pMat->FillDouble( CreateDoubleError(
                                                    NOTAVAILABLE), nResMatCols, 0, nNewCols-1,
                                                    nResMatRows-1);
                                            }
                                            if ( nResMatRows < nNewRows )
                                            {
                                                pMat->FillDouble( CreateDoubleError(
                                                    NOTAVAILABLE), 0, nResMatRows, nNewCols-1,
                                                    nNewRows-1);
                                            }
                                            if ( nRows == 1 && nCurCol != 0 )
                                            {
                                                nCurCol = 0;
                                                nCurRow = nResMatRows - 1;
                                            }
                                            nResMatCols = nNewCols;
                                            nResMatRows = nNewRows;
                                        }
                                    }
};

#endif // SC_JUMPMATRIX_HXX

