#include "rive/shapes/paint/feather.hpp"
#include "rive/shapes/paint/shape_paint.hpp"
#include "rive/shapes/paint/fill.hpp"
#include "rive/shapes/shape.hpp"
#include "rive/artboard.hpp"

#include "rive/core_context.hpp"

using namespace rive;

bool Feather::validate(CoreContext* context)
{
    if (!Super::validate(context))
    {
        return false;
    }
    auto coreObject = context->resolve(parentId());
    // we know it's not nullptr from Super::validate
    assert(coreObject != nullptr);
    return coreObject->is<ShapePaint>();
}

StatusCode Feather::onAddedDirty(CoreContext* context)
{
    auto code = Super::onAddedDirty(context);

    parent()->as<ShapePaint>()->feather(this);
    return code;
}

void Feather::update(ComponentDirt value)
{
    auto shapePaint = parent()->as<ShapePaint>();
    if (hasDirt(value, ComponentDirt::Paint))
    {
        auto renderPaint = shapePaint->renderPaint();
        renderPaint->feather(strength());
    }

    if (hasDirt(value, ComponentDirt::WorldTransform | ComponentDirt::Path))
    {
        bool offsetInArtboard = space() == TransformSpace::world;
        if (inner())
        {
            auto shape = ShapePaintContainer::from(shapePaint->parent());

            if (shape != nullptr)
            {
                auto transform = shape->shapeWorldTransform();
                auto path = shapePaint->pickPath(shape);
                if (path == nullptr)
                {
                    return;
                }
                auto bounds = path->rawPath()->bounds().pad(strength() * 1.5f);
                Vec2D innerOffset(offsetX(), offsetY());
                if (offsetInArtboard)
                {
                    Mat2D inverseTransform = transform.invertOrIdentity();
                    innerOffset =
                        Vec2D::transformDir(innerOffset, inverseTransform);
                }
                m_innerPath.rewind();
                m_innerPath.addRect(bounds);
                Mat2D innerOffsetTransform =
                    Mat2D::fromTranslation(innerOffset);
                m_innerPath.addPathBackwards(*path->rawPath(),
                                             &innerOffsetTransform);
            }
#ifdef TESTING
            renderCount++;
#endif
            return;
        }
    }
}

void Feather::buildDependencies()
{
    auto shape = parent()->as<ShapePaint>()->parent();
    if (shape == nullptr)
    {
        return;
    }
    if (shape->is<Shape>())
    {
        shape->as<Shape>()->pathComposer()->addDependent(this);
    }
    else
    {
        shape->addDependent(this);
    }
}

void Feather::strengthChanged()
{
    addDirt(inner() ? (ComponentDirt)(ComponentDirt::Paint |
                                      ComponentDirt::WorldTransform)
                    : ComponentDirt::Paint);
}
void Feather::offsetXChanged() { strengthChanged(); }
void Feather::offsetYChanged() { strengthChanged(); }