/*
 * Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListRecorder.h>
#include <LibWeb/Painting/SVGSVGPaintable.h>

namespace Web::Painting {

NonnullRefPtr<SVGSVGPaintable> SVGSVGPaintable::create(Layout::SVGSVGBox const& layout_box)
{
    return adopt_ref(*new SVGSVGPaintable(layout_box));
}

SVGSVGPaintable::SVGSVGPaintable(Layout::SVGSVGBox const& layout_box)
    : PaintableBox(layout_box)
{
}

void SVGSVGPaintable::paint_svg_box(DisplayListRecordingContext& context, PaintableBox const& svg_box, PaintPhase phase)
{
    context.display_list_recorder().set_accumulated_visual_context(svg_box.accumulated_visual_context_index());

    // For elements with SVG filters, emit a transparent FillRect to trigger filter application.
    // This ensures content-generating filters (feFlood, feImage) work even with empty source.
    if (auto const& bounds = svg_box.filter().svg_filter_bounds; bounds.has_value()) {
        auto device_rect = context.enclosing_device_rect(*bounds).to_type<int>();
        context.display_list_recorder().fill_rect_transparent(device_rect);
    }

    // Collect masks (SVG <mask>, SVG <clipPath>).
    Vector<DisplayListRecorder::MaskInfo> masks;

    bool skip_painting = false;

    auto mask_area = svg_box.get_mask_area();
    if (mask_area.has_value()) {
        if (mask_area->is_empty()) {
            skip_painting = true;
        } else if (auto mask_display_list = svg_box.calculate_mask(context, *mask_area)) {
            auto rect = context.enclosing_device_rect(*mask_area).to_type<int>();
            auto kind = svg_box.get_mask_type().value_or(Gfx::MaskKind::Alpha);
            masks.append({ mask_display_list, rect, kind });
        }
    }

    auto clip_area = svg_box.get_clip_area();
    if (clip_area.has_value()) {
        if (clip_area->is_empty()) {
            skip_painting = true;
        } else if (auto clip_display_list = svg_box.calculate_clip(context, *clip_area)) {
            auto rect = context.enclosing_device_rect(*clip_area).to_type<int>();
            masks.append({ clip_display_list, rect, Gfx::MaskKind::Alpha });
        }
    }

    context.display_list_recorder().begin_masks(masks);

    if (!skip_painting) {
        svg_box.paint(context, PaintPhase::Foreground);
        paint_descendants(context, svg_box, phase);
    }

    context.display_list_recorder().end_masks(masks);
}

void SVGSVGPaintable::paint_descendants(DisplayListRecordingContext& context, PaintableBox const& paintable, PaintPhase phase)
{
    if (phase != PaintPhase::Foreground)
        return;

    paintable.for_each_child_of_type<PaintableBox>([&](PaintableBox& child) {
        paint_svg_box(context, child, phase);
        return IterationDecision::Continue;
    });
}

}
