/*
 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Library General Public
 License as published by the Free Software Foundation; either
 version 2 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Library General Public License for more details.

 You should have received a copy of the GNU Library General Public License
 along with this library; see the file COPYING.LIB.  If not, write to
 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "TextureMapperLayer.h"

#include "FloatQuad.h"
#include "Region.h"
#include <wtf/MathExtras.h>
#include <wtf/SetForScope.h>

namespace WebCore {

class TextureMapperPaintOptions {
public:
    TextureMapperPaintOptions(TextureMapper& textureMapper)
        : textureMapper(textureMapper)
    { }

    TextureMapper& textureMapper;
    TransformationMatrix transform;
    RefPtr<BitmapTexture> surface;
    float opacity { 1 };
    IntSize offset;
    TextureMapperLayer* backdropLayer { nullptr };
    TextureMapperLayer* replicaLayer { nullptr };
    bool preserves3D { false };
};

TextureMapperLayer::TextureMapperLayer() = default;

TextureMapperLayer::~TextureMapperLayer()
{
    for (auto* child : m_children)
        child->m_parent = nullptr;

    removeFromParent();
}

void TextureMapperLayer::computeTransformsRecursive()
{
    if (m_state.size.isEmpty() && m_state.masksToBounds)
        return;

    // Compute transforms recursively on the way down to leafs.
    {
        TransformationMatrix parentTransform;
        if (m_parent)
            parentTransform = m_parent->m_layerTransforms.combinedForChildren;
        else if (m_effectTarget)
            parentTransform = m_effectTarget->m_layerTransforms.combined;

        const float originX = m_state.anchorPoint.x() * m_state.size.width();
        const float originY = m_state.anchorPoint.y() * m_state.size.height();

        m_layerTransforms.combined = parentTransform;
        m_layerTransforms.combined
            .translate3d(originX + (m_state.pos.x() - m_state.boundsOrigin.x()), originY + (m_state.pos.y() - m_state.boundsOrigin.y()), m_state.anchorPoint.z())
            .multiply(m_layerTransforms.localTransform);

        m_layerTransforms.combinedForChildren = m_layerTransforms.combined;
        m_layerTransforms.combined.translate3d(-originX, -originY, -m_state.anchorPoint.z());

        if (m_isReplica)
            m_layerTransforms.combined.translate(-m_state.pos.x(), -m_state.pos.y());

        if (!m_state.preserves3D)
            m_layerTransforms.combinedForChildren = m_layerTransforms.combinedForChildren.to2dTransform();
        m_layerTransforms.combinedForChildren.multiply(m_state.childrenTransform);
        m_layerTransforms.combinedForChildren.translate3d(-originX, -originY, -m_state.anchorPoint.z());

#if USE(COORDINATED_GRAPHICS)
        // Compute transforms for the future as well.
        TransformationMatrix futureParentTransform;
        if (m_parent)
            futureParentTransform = m_parent->m_layerTransforms.futureCombinedForChildren;
        else if (m_effectTarget)
            futureParentTransform = m_effectTarget->m_layerTransforms.futureCombined;

        m_layerTransforms.futureCombined = futureParentTransform;
        m_layerTransforms.futureCombined
            .translate3d(originX + (m_state.pos.x() - m_state.boundsOrigin.x()), originY + (m_state.pos.y() - m_state.boundsOrigin.y()), m_state.anchorPoint.z())
            .multiply(m_layerTransforms.futureLocalTransform);

        m_layerTransforms.futureCombinedForChildren = m_layerTransforms.futureCombined;
        m_layerTransforms.futureCombined.translate3d(-originX, -originY, -m_state.anchorPoint.z());

        if (!m_state.preserves3D)
            m_layerTransforms.futureCombinedForChildren = m_layerTransforms.futureCombinedForChildren.to2dTransform();
        m_layerTransforms.futureCombinedForChildren.multiply(m_state.childrenTransform);
        m_layerTransforms.futureCombinedForChildren.translate3d(-originX, -originY, -m_state.anchorPoint.z());
#endif
    }

    m_state.visible = m_state.backfaceVisibility || !m_layerTransforms.combined.isBackFaceVisible();

    if (m_parent && m_parent->m_state.preserves3D)
        m_centerZ = m_layerTransforms.combined.mapPoint(FloatPoint3D(m_state.size.width() / 2, m_state.size.height() / 2, 0)).z();

    if (m_state.maskLayer)
        m_state.maskLayer->computeTransformsRecursive();
    if (m_state.replicaLayer)
        m_state.replicaLayer->computeTransformsRecursive();
    if (m_state.backdropLayer)
        m_state.backdropLayer->computeTransformsRecursive();
    for (auto* child : m_children) {
        ASSERT(child->m_parent == this);
        child->computeTransformsRecursive();
    }

    // Reorder children if needed on the way back up.
    if (m_state.preserves3D)
        sortByZOrder(m_children);

#if USE(COORDINATED_GRAPHICS)
    if (m_backingStore && m_animatedBackingStoreClient)
        m_animatedBackingStoreClient->requestBackingStoreUpdateIfNeeded(m_layerTransforms.futureCombined);
#endif
}

void TextureMapperLayer::paint(TextureMapper& textureMapper)
{
    computeTransformsRecursive();

    TextureMapperPaintOptions options(textureMapper);
    options.textureMapper.bindSurface(0);

    paintRecursive(options);
}

void TextureMapperLayer::paintSelf(TextureMapperPaintOptions& options)
{
    if (!m_state.visible || !m_state.contentsVisible)
        return;
    auto targetRect = layerRect();
    if (targetRect.isEmpty())
        return;

    // We apply the following transform to compensate for painting into a surface, and then apply the offset so that the painting fits in the target rect.
    TransformationMatrix transform;
    transform.translate(options.offset.width(), options.offset.height());
    transform.multiply(options.transform);
    transform.multiply(m_layerTransforms.combined);

    TextureMapperSolidColorLayer solidColorLayer;
    TextureMapperBackingStore* backingStore = m_backingStore;
    if (m_state.backgroundColor.isValid()) {
        solidColorLayer.setColor(m_state.backgroundColor);
        backingStore = &solidColorLayer;
    }

    options.textureMapper.setWrapMode(TextureMapper::StretchWrap);
    options.textureMapper.setPatternTransform(TransformationMatrix());

    if (backingStore) {
        backingStore->paintToTextureMapper(options.textureMapper, targetRect, transform, options.opacity);
        if (m_state.showDebugBorders)
            backingStore->drawBorder(options.textureMapper, m_state.debugBorderColor, m_state.debugBorderWidth, targetRect, transform);
        // Only draw repaint count for the main backing store.
        if (m_state.showRepaintCounter)
            backingStore->drawRepaintCounter(options.textureMapper, m_state.repaintCount, m_state.debugBorderColor, targetRect, transform);
    }

    TextureMapperPlatformLayer* contentsLayer = m_contentsLayer;
    if (m_state.solidColor.isValid() && m_state.solidColor.isVisible()) {
        solidColorLayer.setColor(m_state.solidColor);
        contentsLayer = &solidColorLayer;
    }
    if (!contentsLayer)
        return;

    if (!m_state.contentsTileSize.isEmpty()) {
        options.textureMapper.setWrapMode(TextureMapper::RepeatWrap);

        auto patternTransform = TransformationMatrix::rectToRect({ { }, m_state.contentsTileSize }, { { }, m_state.contentsRect.size() })
            .translate(m_state.contentsTilePhase.width() / m_state.contentsRect.width(), m_state.contentsTilePhase.height() / m_state.contentsRect.height());
        options.textureMapper.setPatternTransform(patternTransform);
    }

    bool shouldClip = m_state.contentsClippingRect.isRounded() || !m_state.contentsClippingRect.rect().contains(m_state.contentsRect);
    if (shouldClip) {
        options.textureMapper.beginClip(transform, m_state.contentsClippingRect);
    }

    contentsLayer->paintToTextureMapper(options.textureMapper, m_state.contentsRect, transform, options.opacity);

    if (shouldClip)
        options.textureMapper.endClip();

    if (m_state.showDebugBorders)
        contentsLayer->drawBorder(options.textureMapper, m_state.debugBorderColor, m_state.debugBorderWidth, m_state.contentsRect, transform);
}

void TextureMapperLayer::sortByZOrder(Vector<TextureMapperLayer* >& array)
{
    std::sort(array.begin(), array.end(),
        [](TextureMapperLayer* a, TextureMapperLayer* b) {
            return a->m_centerZ < b->m_centerZ;
        });
}

void TextureMapperLayer::paintSelfAndChildren(TextureMapperPaintOptions& options)
{
    if (m_state.backdropLayer && m_state.backdropLayer == options.backdropLayer)
        return;

    struct Preserves3DScope {
        Preserves3DScope(TextureMapperPaintOptions& passedOptions, bool passedEnable)
            : options(passedOptions)
            , enable(passedEnable)
        {
            if (enable) {
                options.preserves3D = true;
                options.textureMapper.beginPreserves3D();
            }
        }
        ~Preserves3DScope()
        {
            if (enable) {
                options.preserves3D = false;
                options.textureMapper.endPreserves3D();
            }
        }
        TextureMapperPaintOptions& options;
        bool enable;
    } scopedPreserves3D(options, m_state.preserves3D && !options.preserves3D);

    if (m_state.backdropLayer && !options.backdropLayer) {
        TransformationMatrix clipTransform;
        clipTransform.translate(options.offset.width(), options.offset.height());
        clipTransform.multiply(options.transform);
        clipTransform.multiply(m_layerTransforms.combined);
        options.textureMapper.beginClip(clipTransform, m_state.backdropFiltersRect);
        m_state.backdropLayer->paintRecursive(options);
        options.textureMapper.endClip();
    }

    paintSelf(options);

    if (m_children.isEmpty())
        return;

    bool shouldClip = m_state.masksToBounds && !m_state.preserves3D;
    if (shouldClip) {
        TransformationMatrix clipTransform;
        clipTransform.translate(options.offset.width(), options.offset.height());
        clipTransform.multiply(options.transform);
        clipTransform.multiply(m_layerTransforms.combined);
        clipTransform.translate(m_state.boundsOrigin.x(), m_state.boundsOrigin.y());
        options.textureMapper.beginClip(clipTransform, FloatRoundedRect(layerRect()));

        // If as a result of beginClip(), the clipping area is empty, it means that the intersection of the previous
        // clipping area and the current one don't have any pixels in common. In this case we can skip painting the
        // children as they will be clipped out (see https://bugs.webkit.org/show_bug.cgi?id=181080).
        if (options.textureMapper.clipBounds().isEmpty()) {
            options.textureMapper.endClip();
            return;
        }
    }

    for (auto* child : m_children)
        child->paintRecursive(options);

    if (shouldClip)
        options.textureMapper.endClip();
}

bool TextureMapperLayer::shouldBlend() const
{
    if (m_state.preserves3D)
        return false;

    return m_currentOpacity < 1;
}

bool TextureMapperLayer::isVisible() const
{
    if (m_state.size.isEmpty() && (m_state.masksToBounds || m_state.maskLayer || m_children.isEmpty()))
        return false;
    if (!m_state.visible && m_children.isEmpty())
        return false;
    if (!m_state.contentsVisible && m_children.isEmpty())
        return false;
    if (m_currentOpacity < 0.01)
        return false;
    return true;
}

void TextureMapperLayer::paintSelfAndChildrenWithReplica(TextureMapperPaintOptions& options)
{
    if (m_state.replicaLayer) {
        SetForScope scopedReplicaLayer(options.replicaLayer, this);
        SetForScope scopedTransform(options.transform, options.transform);
        options.transform.multiply(replicaTransform());
        paintSelfAndChildren(options);
    }

    paintSelfAndChildren(options);
}

TransformationMatrix TextureMapperLayer::replicaTransform()
{
    return TransformationMatrix(m_state.replicaLayer->m_layerTransforms.combined)
        .multiply(m_layerTransforms.combined.inverse().value_or(TransformationMatrix()));
}

static void resolveOverlaps(const IntRect& newRegion, Region& overlapRegion, Region& nonOverlapRegion)
{
    Region newOverlapRegion(newRegion);
    newOverlapRegion.intersect(nonOverlapRegion);
    nonOverlapRegion.subtract(newOverlapRegion);
    overlapRegion.unite(newOverlapRegion);

    Region newNonOverlapRegion(newRegion);
    newNonOverlapRegion.subtract(overlapRegion);
    nonOverlapRegion.unite(newNonOverlapRegion);
}

void TextureMapperLayer::computeOverlapRegions(ComputeOverlapRegionData& data, const TransformationMatrix& accumulatedReplicaTransform, bool includesReplica)
{
    if (!m_state.visible || !m_state.contentsVisible)
        return;

    FloatRect localBoundingRect;
    if (m_backingStore || m_state.masksToBounds || m_state.maskLayer || hasFilters())
        localBoundingRect = layerRect();
    else if (m_contentsLayer || m_state.solidColor.isVisible())
        localBoundingRect = m_state.contentsRect;

    if (m_currentFilters.hasOutsets() && !m_state.backdropLayer && !m_state.masksToBounds && !m_state.maskLayer) {
        auto outsets = m_currentFilters.outsets();
        localBoundingRect.move(-outsets.left(), -outsets.top());
        localBoundingRect.expand(outsets.left() + outsets.right(), outsets.top() + outsets.bottom());
    }

    TransformationMatrix transform(accumulatedReplicaTransform);
    transform.multiply(m_layerTransforms.combined);

    IntRect viewportBoundingRect = enclosingIntRect(transform.mapRect(localBoundingRect));
    viewportBoundingRect.intersect(data.clipBounds);

    switch (data.mode) {
    case ComputeOverlapRegionMode::Intersection:
        resolveOverlaps(viewportBoundingRect, data.overlapRegion, data.nonOverlapRegion);
        break;
    case ComputeOverlapRegionMode::Union:
    case ComputeOverlapRegionMode::Mask:
        data.overlapRegion.unite(viewportBoundingRect);
        break;
    }

    if (m_state.replicaLayer && includesReplica) {
        TransformationMatrix newReplicaTransform(accumulatedReplicaTransform);
        newReplicaTransform.multiply(replicaTransform());
        computeOverlapRegions(data, newReplicaTransform, false);
    }

    if (!m_state.masksToBounds && data.mode != ComputeOverlapRegionMode::Mask) {
        for (auto* child : m_children)
            child->computeOverlapRegions(data, accumulatedReplicaTransform);
    }
}

void TextureMapperLayer::paintUsingOverlapRegions(TextureMapperPaintOptions& options)
{
    Region overlapRegion;
    Region nonOverlapRegion;
    auto mode = ComputeOverlapRegionMode::Intersection;
    ComputeOverlapRegionData data {
        mode,
        options.textureMapper.clipBounds(),
        overlapRegion,
        nonOverlapRegion
    };
    data.clipBounds.move(-options.offset);
    computeOverlapRegions(data, options.transform);
    if (overlapRegion.isEmpty()) {
        paintSelfChildrenReplicaFilterAndMask(options);
        return;
    }

    // Having both overlap and non-overlap regions carries some overhead. Avoid it if the overlap area
    // is big anyway.
    if (overlapRegion.totalArea() > nonOverlapRegion.totalArea()) {
        overlapRegion.unite(nonOverlapRegion);
        nonOverlapRegion = Region();
    }

    nonOverlapRegion.translate(options.offset);
    auto rects = nonOverlapRegion.rects();

    for (auto& rect : rects) {
        options.textureMapper.beginClip(TransformationMatrix(), FloatRoundedRect(rect));
        paintSelfChildrenReplicaFilterAndMask(options);
        options.textureMapper.endClip();
    }

    rects = overlapRegion.rects();
    static const size_t OverlapRegionConsolidationThreshold = 4;
    if (nonOverlapRegion.isEmpty() && rects.size() > OverlapRegionConsolidationThreshold) {
        rects.clear();
        rects.append(overlapRegion.bounds());
    }

    IntSize maxTextureSize = options.textureMapper.maxTextureSize();
    for (auto& rect : rects) {
        for (int x = rect.x(); x < rect.maxX(); x += maxTextureSize.width()) {
            for (int y = rect.y(); y < rect.maxY(); y += maxTextureSize.height()) {
                IntRect tileRect(IntPoint(x, y), maxTextureSize);
                tileRect.intersect(rect);

                paintWithIntermediateSurface(options, tileRect);
            }
        }
    }
}

void TextureMapperLayer::paintSelfChildrenFilterAndMask(TextureMapperPaintOptions& options)
{
    Region overlapRegion;
    Region nonOverlapRegion;
    auto mode = ComputeOverlapRegionMode::Union;
    if (m_state.maskLayer || (options.replicaLayer == this && m_state.replicaLayer->m_state.maskLayer))
        mode = ComputeOverlapRegionMode::Mask;
    ComputeOverlapRegionData data {
        mode,
        options.textureMapper.clipBounds(),
        overlapRegion,
        nonOverlapRegion
    };
    data.clipBounds.move(-options.offset);
    computeOverlapRegions(data, options.transform, false);
    ASSERT(nonOverlapRegion.isEmpty());

    auto rects = overlapRegion.rects();
    static const size_t OverlapRegionConsolidationThreshold = 4;
    if (rects.size() > OverlapRegionConsolidationThreshold) {
        rects.clear();
        rects.append(overlapRegion.bounds());
    }

    IntSize maxTextureSize = options.textureMapper.maxTextureSize();
    for (auto& rect : rects) {
        for (int x = rect.x(); x < rect.maxX(); x += maxTextureSize.width()) {
            for (int y = rect.y(); y < rect.maxY(); y += maxTextureSize.height()) {
                IntRect tileRect(IntPoint(x, y), maxTextureSize);
                tileRect.intersect(rect);

                paintSelfAndChildrenWithIntermediateSurface(options, tileRect);
            }
        }
    }
}

void TextureMapperLayer::applyMask(TextureMapperPaintOptions& options)
{
    options.textureMapper.setMaskMode(true);
    paintSelf(options);
    options.textureMapper.setMaskMode(false);
}

void TextureMapperLayer::paintIntoSurface(TextureMapperPaintOptions& options)
{
    options.textureMapper.bindSurface(options.surface.get());
    if (m_isBackdrop) {
        SetForScope scopedTransform(options.transform, TransformationMatrix());
        SetForScope scopedReplicaLayer(options.replicaLayer, nullptr);
        SetForScope scopedBackdropLayer(options.backdropLayer, this);
        rootLayer().paintSelfAndChildren(options);
    } else
        paintSelfAndChildren(options);

    bool hasMask = !!m_state.maskLayer;
    bool hasReplicaMask = options.replicaLayer == this && m_state.replicaLayer->m_state.maskLayer;
    bool defersLastFilter = !hasMask && !hasReplicaMask;
    options.surface = options.surface->applyFilters(options.textureMapper, m_currentFilters, defersLastFilter);
    options.textureMapper.bindSurface(options.surface.get());
    if (hasMask)
        m_state.maskLayer->applyMask(options);
    if (hasReplicaMask)
        m_state.replicaLayer->m_state.maskLayer->applyMask(options);
}

static void commitSurface(TextureMapperPaintOptions& options, BitmapTexture& surface, const IntRect& rect, float opacity)
{
    IntRect targetRect(rect);
    targetRect.move(options.offset);
    options.textureMapper.bindSurface(options.surface.get());
    options.textureMapper.drawTexture(surface, targetRect, { }, opacity);
}

void TextureMapperLayer::paintWithIntermediateSurface(TextureMapperPaintOptions& options, const IntRect& rect)
{
    auto surface = options.textureMapper.acquireTextureFromPool(rect.size(), BitmapTexture::SupportsAlpha);
    {
        SetForScope scopedSurface(options.surface, surface);
        SetForScope scopedOffset(options.offset, -toIntSize(rect.location()));
        SetForScope scopedOpacity(options.opacity, 1);

        options.textureMapper.bindSurface(options.surface.get());
        paintSelfChildrenReplicaFilterAndMask(options);
    }

    commitSurface(options, *surface, rect, options.opacity);
}

void TextureMapperLayer::paintSelfAndChildrenWithIntermediateSurface(TextureMapperPaintOptions& options, const IntRect& rect)
{
    auto surface = options.textureMapper.acquireTextureFromPool(rect.size(), BitmapTexture::SupportsAlpha);
    {
        SetForScope scopedSurface(options.surface, surface);
        SetForScope scopedOffset(options.offset, -toIntSize(rect.location()));
        SetForScope scopedOpacity(options.opacity, 1);

        paintIntoSurface(options);
        surface = options.surface;
    }

    commitSurface(options, *surface, rect, options.opacity);
}

void TextureMapperLayer::paintSelfChildrenReplicaFilterAndMask(TextureMapperPaintOptions& options)
{
    bool hasFilterOrMask = [&] {
        if (hasFilters())
            return true;
        if (m_state.maskLayer)
            return true;
        if (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer)
            return true;
        return false;
    }();
    if (hasFilterOrMask) {
        if (m_state.replicaLayer) {
            SetForScope scopedReplicaLayer(options.replicaLayer, this);
            SetForScope scopedTransform(options.transform, options.transform);
            options.transform.multiply(replicaTransform());
            paintSelfChildrenFilterAndMask(options);
        }
        paintSelfChildrenFilterAndMask(options);
    } else
        paintSelfAndChildrenWithReplica(options);
}

void TextureMapperLayer::paintRecursive(TextureMapperPaintOptions& options)
{
    if (!isVisible())
        return;

    SetForScope scopedOpacity(options.opacity, options.opacity * m_currentOpacity);

    if (shouldBlend())
        paintUsingOverlapRegions(options);
    else
        paintSelfChildrenReplicaFilterAndMask(options);
}

void TextureMapperLayer::setChildren(const Vector<TextureMapperLayer*>& newChildren)
{
    removeAllChildren();
    for (auto* child : newChildren)
        addChild(child);
}

void TextureMapperLayer::addChild(TextureMapperLayer* childLayer)
{
    ASSERT(childLayer != this);

    if (childLayer->m_parent)
        childLayer->removeFromParent();

    childLayer->m_parent = this;
    m_children.append(childLayer);
}

void TextureMapperLayer::removeFromParent()
{
    if (m_parent) {
        size_t index = m_parent->m_children.find(this);
        ASSERT(index != notFound);
        m_parent->m_children.remove(index);
    }

    m_parent = nullptr;
}

void TextureMapperLayer::removeAllChildren()
{
    auto oldChildren = WTFMove(m_children);
    for (auto* child : oldChildren)
        child->m_parent = nullptr;
}

void TextureMapperLayer::setMaskLayer(TextureMapperLayer* maskLayer)
{
    if (maskLayer) {
        maskLayer->m_effectTarget = m_isReplica ? m_effectTarget : *this;
        m_state.maskLayer = *maskLayer;
    } else
        m_state.maskLayer = nullptr;
}

void TextureMapperLayer::setReplicaLayer(TextureMapperLayer* replicaLayer)
{
    if (replicaLayer) {
        replicaLayer->m_isReplica = true;
        replicaLayer->m_effectTarget = *this;
        m_state.replicaLayer = *replicaLayer;
    } else
        m_state.replicaLayer = nullptr;
}

void TextureMapperLayer::setBackdropLayer(TextureMapperLayer* backdropLayer)
{
    if (backdropLayer) {
        backdropLayer->m_isBackdrop = true;
        backdropLayer->m_effectTarget = *this;
        m_state.backdropLayer = *backdropLayer;
    } else
        m_state.backdropLayer = nullptr;
}

void TextureMapperLayer::setBackdropFiltersRect(const FloatRoundedRect& backdropFiltersRect)
{
    m_state.backdropFiltersRect = backdropFiltersRect;
}

void TextureMapperLayer::setPosition(const FloatPoint& position)
{
    m_state.pos = position;
}

void TextureMapperLayer::setBoundsOrigin(const FloatPoint& boundsOrigin)
{
    m_state.boundsOrigin = boundsOrigin;
}

void TextureMapperLayer::setSize(const FloatSize& size)
{
    m_state.size = size;
}

void TextureMapperLayer::setAnchorPoint(const FloatPoint3D& anchorPoint)
{
    m_state.anchorPoint = anchorPoint;
}

void TextureMapperLayer::setPreserves3D(bool preserves3D)
{
    m_state.preserves3D = preserves3D;
}

void TextureMapperLayer::setTransform(const TransformationMatrix& transform)
{
    m_state.transform = transform;
}

void TextureMapperLayer::setChildrenTransform(const TransformationMatrix& childrenTransform)
{
    m_state.childrenTransform = childrenTransform;
}

void TextureMapperLayer::setContentsRect(const FloatRect& contentsRect)
{
    m_state.contentsRect = contentsRect;
}

void TextureMapperLayer::setContentsTileSize(const FloatSize& size)
{
    m_state.contentsTileSize = size;
}

void TextureMapperLayer::setContentsTilePhase(const FloatSize& phase)
{
    m_state.contentsTilePhase = phase;
}

void TextureMapperLayer::setContentsClippingRect(const FloatRoundedRect& contentsClippingRect)
{
    m_state.contentsClippingRect = contentsClippingRect;
}

void TextureMapperLayer::setMasksToBounds(bool masksToBounds)
{
    m_state.masksToBounds = masksToBounds;
}

void TextureMapperLayer::setDrawsContent(bool drawsContent)
{
    m_state.drawsContent = drawsContent;
}

void TextureMapperLayer::setContentsVisible(bool contentsVisible)
{
    m_state.contentsVisible = contentsVisible;
}

void TextureMapperLayer::setContentsOpaque(bool contentsOpaque)
{
    m_state.contentsOpaque = contentsOpaque;
}

void TextureMapperLayer::setBackfaceVisibility(bool backfaceVisibility)
{
    m_state.backfaceVisibility = backfaceVisibility;
}

void TextureMapperLayer::setOpacity(float opacity)
{
    m_state.opacity = opacity;
}

void TextureMapperLayer::setSolidColor(const Color& color)
{
    m_state.solidColor = color;
}

void TextureMapperLayer::setBackgroundColor(const Color& color)
{
    m_state.backgroundColor = color;
}

void TextureMapperLayer::setFilters(const FilterOperations& filters)
{
    m_state.filters = filters;
}

void TextureMapperLayer::setDebugVisuals(bool showDebugBorders, const Color& debugBorderColor, float debugBorderWidth)
{
    m_state.showDebugBorders = showDebugBorders;
    m_state.debugBorderColor = debugBorderColor;
    m_state.debugBorderWidth = debugBorderWidth;
}

void TextureMapperLayer::setRepaintCounter(bool showRepaintCounter, int repaintCount)
{
    m_state.showRepaintCounter = showRepaintCounter;
    m_state.repaintCount = repaintCount;
}

void TextureMapperLayer::setContentsLayer(TextureMapperPlatformLayer* platformLayer)
{
    m_contentsLayer = platformLayer;
}

void TextureMapperLayer::setAnimations(const Nicosia::Animations& animations)
{
    m_animations = animations;
}

void TextureMapperLayer::setBackingStore(TextureMapperBackingStore* backingStore)
{
    m_backingStore = backingStore;
}

#if USE(COORDINATED_GRAPHICS)
void TextureMapperLayer::setAnimatedBackingStoreClient(Nicosia::AnimatedBackingStoreClient* client)
{
    m_animatedBackingStoreClient = client;
}
#endif

bool TextureMapperLayer::descendantsOrSelfHaveRunningAnimations() const
{
    if (m_animations.hasRunningAnimations())
        return true;

    return std::any_of(m_children.begin(), m_children.end(),
        [](TextureMapperLayer* child) {
            return child->descendantsOrSelfHaveRunningAnimations();
        });
}

bool TextureMapperLayer::applyAnimationsRecursively(MonotonicTime time)
{
    bool hasRunningAnimations = syncAnimations(time);
    if (m_state.replicaLayer)
        hasRunningAnimations |= m_state.replicaLayer->applyAnimationsRecursively(time);
    if (m_state.backdropLayer)
        hasRunningAnimations |= m_state.backdropLayer->syncAnimations(time);
    for (auto* child : m_children)
        hasRunningAnimations |= child->applyAnimationsRecursively(time);
    return hasRunningAnimations;
}

bool TextureMapperLayer::syncAnimations(MonotonicTime time)
{
    Nicosia::Animation::ApplicationResult applicationResults;
    m_animations.apply(applicationResults, time);

    m_layerTransforms.localTransform = applicationResults.transform.value_or(m_state.transform);
    m_currentOpacity = applicationResults.opacity.value_or(m_state.opacity);
    m_currentFilters = applicationResults.filters.value_or(m_state.filters);

#if USE(COORDINATED_GRAPHICS)
    // Calculate localTransform 50ms in the future.
    Nicosia::Animation::ApplicationResult futureApplicationResults;
    m_animations.applyKeepingInternalState(futureApplicationResults, time + 50_ms);
    m_layerTransforms.futureLocalTransform = futureApplicationResults.transform.value_or(m_layerTransforms.localTransform);
#endif

    return applicationResults.hasRunningAnimations;
}

}
