import SwiftUI

struct FlowLayout: Layout {
    var spacing: CGFloat = 4

    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize {
        let result = arrange(proposal: proposal, subviews: subviews)
        return result.size
    }

    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) {
        let result = arrange(proposal: proposal, subviews: subviews)
        for (index, position) in result.positions.enumerated() {
            subviews[index].place(
                at: CGPoint(x: bounds.minX + position.x, y: bounds.minY + position.y),
                proposal: .unspecified,
            )
        }
    }

    private func arrange(proposal: ProposedViewSize, subviews: Subviews) -> (positions: [CGPoint], size: CGSize) {
        let maxWidth = proposal.width ?? .infinity
        var positions: [CGPoint] = []
        var xPos: CGFloat = 0
        var yPos: CGFloat = 0
        var rowHeight: CGFloat = 0
        var totalHeight: CGFloat = 0
        var totalWidth: CGFloat = 0

        for subview in subviews {
            let size = subview.sizeThatFits(.unspecified)
            if xPos + size.width > maxWidth, xPos > 0 {
                xPos = 0
                yPos += rowHeight + spacing
                rowHeight = 0
            }
            positions.append(CGPoint(x: xPos, y: yPos))
            rowHeight = max(rowHeight, size.height)
            xPos += size.width + spacing
            totalWidth = max(totalWidth, xPos - spacing)
            totalHeight = yPos + rowHeight
        }

        return (positions, CGSize(width: totalWidth, height: totalHeight))
    }
}
