/*
 * Copyright (c) 2021-2026, Sam Atkins <sam@ladybird.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/NonnullOwnPtr.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibGC/Ptr.h>
#include <LibWeb/Forward.h>

namespace Web::CSS {

// Corresponds to Kleene 3-valued logic.
enum class MatchResult : u8 {
    False,
    True,
    Unknown,
};

constexpr MatchResult operator&&(MatchResult const a, MatchResult const b)
{
    // If either is false, false.
    if (a == MatchResult::False || b == MatchResult::False)
        return MatchResult::False;
    // If both are true, true.
    if (a == MatchResult::True && b == MatchResult::True)
        return MatchResult::True;
    // Otherwise, unknown.
    return MatchResult::Unknown;
}

constexpr MatchResult operator||(MatchResult const a, MatchResult const b)
{
    // If either is true, true.
    if (a == MatchResult::True || b == MatchResult::True)
        return MatchResult::True;
    // If both are false, false.
    if (a == MatchResult::False && b == MatchResult::False)
        return MatchResult::False;
    // Otherwise, unknown.
    return MatchResult::Unknown;
}

constexpr MatchResult as_match_result(bool value)
{
    return value ? MatchResult::True : MatchResult::False;
}

constexpr MatchResult negate(MatchResult value)
{
    switch (value) {
    case MatchResult::False:
        return MatchResult::True;
    case MatchResult::True:
        return MatchResult::False;
    case MatchResult::Unknown:
        return MatchResult::Unknown;
    }
    VERIFY_NOT_REACHED();
}

constexpr StringView to_string(MatchResult result)
{
    switch (result) {
    case MatchResult::False:
        return "false"sv;
    case MatchResult::True:
        return "true"sv;
    case MatchResult::Unknown:
        return "unknown"sv;
    }
    VERIFY_NOT_REACHED();
}

struct BooleanExpressionEvaluationContext {
    GC::Ptr<DOM::Document const> document { nullptr };
    GC::Ptr<DOM::Element const> query_container { nullptr };
};

struct ContainerQueryFeatureRequirements {
    bool requires_size_container : 1 { false };
    bool requires_inline_size_container : 1 { false };
    bool requires_block_size_container : 1 { false };
    bool requires_style_container : 1 { false };
    bool requires_scroll_state_container : 1 { false };
    bool has_unknown_or_unsupported_feature : 1 { false };
};

// The contents of this file implement the `<boolean-expr>` concept.
// https://drafts.csswg.org/css-values-5/#typedef-boolean-expr
class BooleanExpression {
public:
    virtual ~BooleanExpression() = default;

    bool evaluate_to_boolean(BooleanExpressionEvaluationContext const&) const;
    static void indent(StringBuilder& builder, int levels);

    virtual MatchResult evaluate(BooleanExpressionEvaluationContext const&) const = 0;
    virtual void collect_container_query_feature_requirements(ContainerQueryFeatureRequirements&) const { }
    virtual String to_string() const = 0;
    virtual void dump(StringBuilder&, int indent_levels = 0) const = 0;
};

// https://www.w3.org/TR/mediaqueries-4/#typedef-general-enclosed
class GeneralEnclosed final : public BooleanExpression {
public:
    static NonnullOwnPtr<GeneralEnclosed> create(String serialized_contents, MatchResult matches = MatchResult::Unknown)
    {
        return adopt_own(*new GeneralEnclosed(move(serialized_contents), matches));
    }
    virtual ~GeneralEnclosed() override = default;

    virtual MatchResult evaluate(BooleanExpressionEvaluationContext const&) const override { return m_matches; }
    virtual void collect_container_query_feature_requirements(ContainerQueryFeatureRequirements&) const override;
    virtual String to_string() const override { return m_serialized_contents; }
    virtual void dump(StringBuilder&, int indent_levels = 0) const override;

private:
    GeneralEnclosed(String serialized_contents, MatchResult matches)
        : m_serialized_contents(move(serialized_contents))
        , m_matches(matches)
    {
    }

    String m_serialized_contents;
    MatchResult m_matches;
};

class BooleanNotExpression final : public BooleanExpression {
public:
    static NonnullOwnPtr<BooleanNotExpression> create(NonnullOwnPtr<BooleanExpression>&& child)
    {
        return adopt_own(*new BooleanNotExpression(move(child)));
    }
    virtual ~BooleanNotExpression() override = default;

    virtual MatchResult evaluate(BooleanExpressionEvaluationContext const&) const override;
    virtual void collect_container_query_feature_requirements(ContainerQueryFeatureRequirements&) const override;
    virtual String to_string() const override;
    virtual void dump(StringBuilder&, int indent_levels = 0) const override;

private:
    BooleanNotExpression(NonnullOwnPtr<BooleanExpression>&& child)
        : m_child(move(child))
    {
    }

    NonnullOwnPtr<BooleanExpression> m_child;
};

class BooleanExpressionInParens final : public BooleanExpression {
public:
    static NonnullOwnPtr<BooleanExpressionInParens> create(NonnullOwnPtr<BooleanExpression>&& child)
    {
        return adopt_own(*new BooleanExpressionInParens(move(child)));
    }
    virtual ~BooleanExpressionInParens() override = default;

    virtual MatchResult evaluate(BooleanExpressionEvaluationContext const&) const override;
    virtual void collect_container_query_feature_requirements(ContainerQueryFeatureRequirements&) const override;
    virtual String to_string() const override;
    virtual void dump(StringBuilder&, int indent_levels = 0) const override;

private:
    BooleanExpressionInParens(NonnullOwnPtr<BooleanExpression>&& child)
        : m_child(move(child))
    {
    }

    NonnullOwnPtr<BooleanExpression> m_child;
};

class BooleanAndExpression final : public BooleanExpression {
public:
    static NonnullOwnPtr<BooleanAndExpression> create(Vector<NonnullOwnPtr<BooleanExpression>>&& children)
    {
        return adopt_own(*new BooleanAndExpression(move(children)));
    }
    virtual ~BooleanAndExpression() override = default;

    virtual MatchResult evaluate(BooleanExpressionEvaluationContext const&) const override;
    virtual void collect_container_query_feature_requirements(ContainerQueryFeatureRequirements&) const override;
    virtual String to_string() const override;
    virtual void dump(StringBuilder&, int indent_levels = 0) const override;

private:
    BooleanAndExpression(Vector<NonnullOwnPtr<BooleanExpression>>&& children)
        : m_children(move(children))
    {
    }

    Vector<NonnullOwnPtr<BooleanExpression>> m_children;
};

class BooleanOrExpression final : public BooleanExpression {
public:
    static NonnullOwnPtr<BooleanOrExpression> create(Vector<NonnullOwnPtr<BooleanExpression>>&& children)
    {
        return adopt_own(*new BooleanOrExpression(move(children)));
    }
    virtual ~BooleanOrExpression() override = default;

    virtual MatchResult evaluate(BooleanExpressionEvaluationContext const&) const override;
    virtual void collect_container_query_feature_requirements(ContainerQueryFeatureRequirements&) const override;
    virtual String to_string() const override;
    virtual void dump(StringBuilder&, int indent_levels = 0) const override;

private:
    BooleanOrExpression(Vector<NonnullOwnPtr<BooleanExpression>>&& children)
        : m_children(move(children))
    {
    }

    Vector<NonnullOwnPtr<BooleanExpression>> m_children;
};

class ConstantBooleanExpression final : public BooleanExpression {
public:
    static NonnullOwnPtr<ConstantBooleanExpression> create(MatchResult value)
    {
        return adopt_own(*new ConstantBooleanExpression(value));
    }
    virtual ~ConstantBooleanExpression() override = default;

    virtual MatchResult evaluate(BooleanExpressionEvaluationContext const&) const override { return m_value; }
    virtual String to_string() const override { return MUST(String::from_utf8(CSS::to_string(m_value))); }
    virtual void dump(StringBuilder&, int indent_levels = 0) const override;

private:
    ConstantBooleanExpression(MatchResult value)
        : m_value(value)
    {
    }

    MatchResult m_value;
};

}

template<>
struct AK::Formatter<Web::CSS::BooleanExpression> : AK::Formatter<StringView> {
    ErrorOr<void> format(FormatBuilder& builder, Web::CSS::BooleanExpression const& expression)
    {
        return Formatter<StringView>::format(builder, expression.to_string());
    }
};
