/*
 *  SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef __KIS_FULL_REFRESH_WALKER_H
#define __KIS_FULL_REFRESH_WALKER_H

#include "kis_merge_walker.h"
#include "kis_refresh_subtree_walker.h"


class KisFullRefreshWalker : public KisRefreshSubtreeWalker, public KisMergeWalker
{
public:
    using KisRefreshSubtreeWalker::Flag;
    using KisRefreshSubtreeWalker::Flags;

public:
    KisFullRefreshWalker(QRect cropRect, Flags flags = None)
        : KisRefreshSubtreeWalker(cropRect, flags | DontAdjustChangeRect),
          KisMergeWalker(cropRect, NO_FILTHY | (flags.testFlag(ClonesDontInvalidateFrames) ? CLONES_DONT_INVALIDATE_FRAMES : DEFAULT))
    {
    }

    UpdateType type() const override {
        return KisRefreshSubtreeWalker::flags() & NoFilthyMode ?
            FULL_REFRESH_NO_FILTHY : FULL_REFRESH;
    }

    void startTrip(KisProjectionLeafSP startWith) override {
        if(m_firstRun) {
            m_firstRun = false;

            m_currentUpdateType = UPDATE;
            KisMergeWalker::startTrip(startWith);

            m_currentUpdateType = FULL_REFRESH;

            if (!startWith->dependsOnLowerNodes()) {
                KisRefreshSubtreeWalker::startTrip(startWith);
            } else {
                KIS_SAFE_ASSERT_RECOVER_NOOP(!startWith->canHaveChildLayers());
            }

            m_firstRun = true;
        }
        else {
            if(m_currentUpdateType == FULL_REFRESH) {
                KisRefreshSubtreeWalker::startTrip(startWith);
            }
            else {
                KisMergeWalker::startTrip(startWith);
            }
        }
    }

    void registerChangeRect(KisProjectionLeafSP leaf, NodePosition position) override {
        if(m_currentUpdateType == FULL_REFRESH) {
            KisRefreshSubtreeWalker::registerChangeRect(leaf, position);
        }
        else {
            /**
             * Merge walker thinks that we changed the original of the
             * dirty node (dirtyNode == startNode()), but that is not
             * true in case of full refresh walker, because all the
             * children of the dirty node are dirty as well, that is
             * why we shouldn't rely on usual registerChangeRect()
             * mechanism for this node. That is why we just unite the
             * changeRects of all its children here.
             */

            if(isStartLeaf(leaf)&& !leaf->isRoot()) {
                QRect changeRect;
                bool changeRectVaries = false;
                std::tie(changeRect, changeRectVaries) =
                    KisRefreshSubtreeWalker::calculateChangeRect(leaf, requestedRect());
                setExplicitChangeRect(changeRect, changeRectVaries);
            }
            else {
                KisMergeWalker::registerChangeRect(leaf, position);
            }
        }
    }
    void registerNeedRect(KisProjectionLeafSP leaf, NodePosition position, KisRenderPassFlags renderFlags, const QRect &cropRect) override {
        if(m_currentUpdateType == FULL_REFRESH) {
            KisRefreshSubtreeWalker::registerNeedRect(leaf, position, renderFlags, cropRect);
        }
        else {
            KisMergeWalker::registerNeedRect(leaf, position, renderFlags, cropRect);
        }
    }
    void adjustMasksChangeRect(KisProjectionLeafSP firstMask) override {
        if(m_currentUpdateType == FULL_REFRESH) {
            KisRefreshSubtreeWalker::adjustMasksChangeRect(firstMask);
        }
        else {
            KisMergeWalker::adjustMasksChangeRect(firstMask);
        }
    }

private:
    UpdateType m_currentUpdateType { UPDATE };
    bool m_firstRun { true };
};

#endif /* __KIS_FULL_REFRESH_WALKER_H */
