/*
 * Copyright (c) 2023, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/HashMap.h>
#include <LibWeb/Layout/Box.h>

namespace Web::Layout {

class TableGrid {
public:
    struct GridPosition {
        size_t x;
        size_t y;
        inline bool operator==(GridPosition const&) const = default;
    };

    struct Row {
        JS::NonnullGCPtr<Box const> box;
        CSSPixels base_height { 0 };
        CSSPixels reference_height { 0 };
        CSSPixels final_height { 0 };
        CSSPixels baseline { 0 };
        CSSPixels min_size { 0 };
        CSSPixels max_size { 0 };
        bool has_intrinsic_percentage { false };
        double intrinsic_percentage { 0 };
        // Store whether the row is constrained: https://www.w3.org/TR/css-tables-3/#constrainedness
        bool is_constrained { false };
    };

    struct Cell {
        JS::NonnullGCPtr<Box const> box;
        size_t column_index;
        size_t row_index;
        size_t column_span;
        size_t row_span;
        CSSPixels baseline { 0 };
        CSSPixels outer_min_width { 0 };
        CSSPixels outer_max_width { 0 };
        CSSPixels outer_min_height { 0 };
        CSSPixels outer_max_height { 0 };
    };

    // Calculate and return the grid and also rows and cells as output parameters.
    static TableGrid calculate_row_column_grid(Box const&, Vector<Cell>&, Vector<Row>&);
    // Overload for callers that don't care about rows and cells (currently the layout tree builder).
    static TableGrid calculate_row_column_grid(Box const& box);

    size_t column_count() const { return m_column_count; }
    HashMap<GridPosition, bool> const& occupancy_grid() const { return m_occupancy_grid; }

    static bool is_table_row_group(Box const& box)
    {
        auto const& display = box.display();
        return display.is_table_row_group() || display.is_table_header_group() || display.is_table_footer_group();
    }

    static bool is_table_row(Box const& box)
    {
        return box.display().is_table_row();
    }

    static bool is_table_column_group(Box const& box)
    {
        return box.display().is_table_column_group();
    }

    template<typename Matcher, typename Callback>
    static void for_each_child_box_matching(Box const& parent, Matcher matcher, Callback callback)
    {
        parent.for_each_child_of_type<Box>([&](Box const& child_box) {
            if (matcher(child_box))
                callback(child_box);
            return IterationDecision::Continue;
        });
    }

private:
    size_t m_column_count { 0 };
    HashMap<GridPosition, bool> m_occupancy_grid;
};

}

namespace AK {

template<>
struct Traits<Web::Layout::TableGrid::GridPosition> : public DefaultTraits<Web::Layout::TableGrid::GridPosition> {
    static unsigned hash(Web::Layout::TableGrid::GridPosition const& key)
    {
        return pair_int_hash(key.x, key.y);
    }
};

}