/**************************************************************
 *
 * 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 INCLUDED_RECURSIONHELPER_HXX
#define INCLUDED_RECURSIONHELPER_HXX

#include "formularesult.hxx"

#include <list>
#include <stack>
#include <tools/solar.h>

class ScFormulaCell;

struct ScFormulaRecursionEntry
{
    ScFormulaCell*  pCell;
    sal_Bool            bOldRunning;
    ScFormulaResult aPreviousResult;
    ScFormulaRecursionEntry( ScFormulaCell* p, sal_Bool bR,
            const ScFormulaResult & rRes ) :
        pCell(p), bOldRunning(bR), aPreviousResult( rRes)
    {
    }
};

typedef ::std::list< ScFormulaRecursionEntry > ScFormulaRecursionList;

class ScRecursionHelper
{
    typedef ::std::stack< ScFormulaCell* >  ScRecursionInIterationStack;
    ScFormulaRecursionList              aRecursionFormulas;
    ScFormulaRecursionList::iterator    aInsertPos;
    ScFormulaRecursionList::iterator    aLastIterationStart;
    ScRecursionInIterationStack         aRecursionInIterationStack;
    sal_uInt16                              nRecursionCount;
    sal_uInt16                              nIteration;
    bool                                bInRecursionReturn;
    bool                                bDoingRecursion;
    bool                                bInIterationReturn;
    bool                                bConverging;

    void    Init()
    {
        nRecursionCount    = 0;
        bInRecursionReturn = bDoingRecursion = bInIterationReturn = false;
        aInsertPos = GetEnd();
        ResetIteration();
    }
    void    ResetIteration()
    {
        aLastIterationStart = GetEnd();
        nIteration = 0;
        bConverging = false;
    }

    public:

    ScRecursionHelper() { Init(); }
    sal_uInt16  GetRecursionCount() const       { return nRecursionCount; }
    void    IncRecursionCount()             { ++nRecursionCount; }
    void    DecRecursionCount()             { --nRecursionCount; }
    /// A pure recursion return, no iteration.
    bool    IsInRecursionReturn() const     { return bInRecursionReturn &&
        !bInIterationReturn; }
    void    SetInRecursionReturn( bool b )
    {
        // Do not use IsInRecursionReturn() here, it decouples iteration.
        if (b && !bInRecursionReturn)
            aInsertPos = aRecursionFormulas.begin();
        bInRecursionReturn = b;
    }
    bool    IsDoingRecursion() const        { return bDoingRecursion; }
    void    SetDoingRecursion( bool b )     { bDoingRecursion = b; }
    void    Insert( ScFormulaCell* p, sal_Bool bOldRunning,
                    const ScFormulaResult & rRes )
    {
        aRecursionFormulas.insert( aInsertPos, ScFormulaRecursionEntry( p,
                    bOldRunning, rRes));
    }
    ScFormulaRecursionList::iterator    GetStart()
    {
        return aRecursionFormulas.begin();
    }
    ScFormulaRecursionList::iterator    GetEnd()
    {
        return aRecursionFormulas.end();
    }
    bool    IsInIterationReturn() const     { return bInIterationReturn; }
    void    SetInIterationReturn( bool b )
    {
        // An iteration return is always coupled to a recursion return.
        SetInRecursionReturn( b);
        bInIterationReturn = b;
    }
    bool    IsDoingIteration() const        { return nIteration > 0; }
    sal_uInt16  GetIteration() const            { return nIteration; }
    bool &  GetConvergingReference()        { return bConverging; }
    void    StartIteration()
    {
        SetInIterationReturn( false);
        nIteration = 1;
        bConverging = false;
        aLastIterationStart = GetIterationStart();
    }
    void    ResumeIteration()
    {
        SetInIterationReturn( false);
        aLastIterationStart = GetIterationStart();
    }
    void    IncIteration()                  { ++nIteration; }
    void    EndIteration()
    {
        aRecursionFormulas.erase( GetIterationStart(), GetIterationEnd());
        ResetIteration();
    }
    ScFormulaRecursionList::iterator GetLastIterationStart() { return aLastIterationStart; }
    ScFormulaRecursionList::iterator GetIterationStart() { return GetStart(); }
    ScFormulaRecursionList::iterator GetIterationEnd() { return GetEnd(); }
    /** Any return, recursion or iteration, iteration is always coupled with
        recursion. */
    bool    IsInReturn() const              { return bInRecursionReturn; }
    const ScFormulaRecursionList&   GetList() const { return aRecursionFormulas; }
    ScFormulaRecursionList&         GetList()       { return aRecursionFormulas; }
    ScRecursionInIterationStack&    GetRecursionInIterationStack()  { return aRecursionInIterationStack; }
    void    Clear()
    {
        aRecursionFormulas.clear();
        while (!aRecursionInIterationStack.empty())
            aRecursionInIterationStack.pop();
        Init();
    }
};

#endif // INCLUDED_RECURSIONHELPER_HXX