/*
 * Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
 * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
 * Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
 * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/Function.h>
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/Flex.h>
#include <LibWeb/CSS/Frequency.h>
#include <LibWeb/CSS/Length.h>
#include <LibWeb/CSS/Number.h>
#include <LibWeb/CSS/NumericRange.h>
#include <LibWeb/CSS/NumericType.h>
#include <LibWeb/CSS/Percentage.h>
#include <LibWeb/CSS/Resolution.h>
#include <LibWeb/CSS/StyleValues/AbstractNonMathCalcFunctionStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValue.h>
#include <LibWeb/CSS/Time.h>

namespace Web::CSS {

// https://drafts.csswg.org/css-values-4/#calc-context
// Contains the context available at parse-time.
struct CalculationContext {
    Optional<ValueType> percentages_resolve_as {};
    bool resolve_numbers_as_integers = false;
    // FIXME: Once calc() parsing knows the target numeric type, pass a single NumericRange instead of the full accepted range set.
    NumericRangesByValueType accepted_ranges_by_type {};

    static CalculationContext for_property(PropertyNameAndID const&);
};

class CalculatedStyleValue : public StyleValue {
public:
    class CalculationResult {
    public:
        using Value = Variant<Number, Angle, Flex, Frequency, Length, Percentage, Resolution, Time>;
        static CalculationResult from_value(Value const&, CalculationResolutionContext const&, Optional<NumericType>);

        CalculationResult(double value, Optional<NumericType> type)
            : m_value(value)
            , m_type(move(type))
        {
        }

        void add(CalculationResult const& other);
        void subtract(CalculationResult const& other);
        void multiply_by(CalculationResult const& other);
        void divide_by(CalculationResult const& other);
        void negate();
        void invert();

        double value() const { return m_value; }
        Optional<NumericType> const& type() const { return m_type; }

        [[nodiscard]] bool operator==(CalculationResult const&) const = default;

    private:
        double m_value;
        Optional<NumericType> m_type;
    };

    static ValueComparingNonnullRefPtr<CalculatedStyleValue const> create(NonnullRefPtr<CalculationNode const> calculation, NumericType resolved_type, CalculationContext context)
    {
        return adopt_ref(*new (nothrow) CalculatedStyleValue(move(calculation), move(resolved_type), move(context)));
    }

    virtual void serialize(StringBuilder&, SerializationMode) const override;
    virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
    virtual bool equals(StyleValue const& other) const override;
    virtual bool is_computationally_independent() const override;

    NonnullRefPtr<CalculationNode const> calculation() const { return m_calculation; }

    bool resolves_to_angle() const { return m_resolved_type.matches_angle(m_context.percentages_resolve_as); }
    bool resolves_to_angle_percentage() const { return m_resolved_type.matches_angle_percentage(m_context.percentages_resolve_as); }
    Optional<Angle> resolve_angle(CalculationResolutionContext const&) const;

    bool resolves_to_flex() const { return m_resolved_type.matches_flex(m_context.percentages_resolve_as); }
    Optional<Flex> resolve_flex(CalculationResolutionContext const&) const;

    bool resolves_to_frequency() const { return m_resolved_type.matches_frequency(m_context.percentages_resolve_as); }
    bool resolves_to_frequency_percentage() const { return m_resolved_type.matches_frequency_percentage(m_context.percentages_resolve_as); }
    Optional<Frequency> resolve_frequency(CalculationResolutionContext const&) const;

    bool resolves_to_length() const { return m_resolved_type.matches_length(m_context.percentages_resolve_as); }
    bool resolves_to_length_percentage() const { return m_resolved_type.matches_length_percentage(m_context.percentages_resolve_as); }
    Optional<Length> resolve_length(CalculationResolutionContext const&) const;
    Optional<double> resolve_raw_length(CalculationResolutionContext const&) const;

    bool resolves_to_percentage() const { return m_resolved_type.matches_percentage(); }
    Optional<Percentage> resolve_percentage(CalculationResolutionContext const&) const;

    bool resolves_to_resolution() const { return m_resolved_type.matches_resolution(m_context.percentages_resolve_as); }
    Optional<Resolution> resolve_resolution(CalculationResolutionContext const&) const;

    bool resolves_to_time() const { return m_resolved_type.matches_time(m_context.percentages_resolve_as); }
    bool resolves_to_time_percentage() const { return m_resolved_type.matches_time_percentage(m_context.percentages_resolve_as); }
    Optional<Time> resolve_time(CalculationResolutionContext const&) const;

    bool resolves_to_number() const { return m_resolved_type.matches_number(m_context.percentages_resolve_as); }
    Optional<double> resolve_number(CalculationResolutionContext const&) const;
    Optional<i32> resolve_integer(CalculationResolutionContext const&) const;

    bool resolves_to_dimension() const { return m_resolved_type.matches_dimension(); }

    bool contains_percentage() const;

    String dump() const;

    virtual GC::Ref<CSSStyleValue> reify(JS::Realm&, FlyString const& associated_property) const override;

private:
    explicit CalculatedStyleValue(NonnullRefPtr<CalculationNode const> calculation, NumericType resolved_type, CalculationContext context)
        : StyleValue(Type::Calculated)
        , m_resolved_type(move(resolved_type))
        , m_calculation(move(calculation))
        , m_context(move(context))
    {
    }

    struct ResolvedValue {
        double value;
        Optional<NumericType> type;
    };
    // FIXME: Calculations should be simplified apart from percentages by the absolutized method prior to this method
    //        being called so we can take just the percentage_basis rather than a full CalculationResolutionContext.
    //        There are still some CalculatedStyleValues which we don't call absolutized for (i.e. sub-values of other
    //        StyleValue classes which lack their own absolutized method) which will need to be fixed beforehand.
    Optional<ResolvedValue> resolve_value(CalculationResolutionContext const&, bool apply_censoring_and_clamping = true) const;

    Optional<ValueType> percentage_resolved_type() const;

    NumericType m_resolved_type;
    NonnullRefPtr<CalculationNode const> m_calculation;
    CalculationContext m_context;
};

#define ENUMERATE_CALCULATION_NODE_TYPES(X) \
    X(Numeric)                              \
    X(Min)                                  \
    X(Max)                                  \
    X(Clamp)                                \
    X(Sum)                                  \
    X(Product)                              \
    X(Negate)                               \
    X(Invert)                               \
    X(Abs)                                  \
    X(Sign)                                 \
    X(Sin)                                  \
    X(Cos)                                  \
    X(Tan)                                  \
    X(Asin)                                 \
    X(Acos)                                 \
    X(Atan)                                 \
    X(Atan2)                                \
    X(Pow)                                  \
    X(Sqrt)                                 \
    X(Hypot)                                \
    X(Log)                                  \
    X(Exp)                                  \
    X(Round)                                \
    X(Mod)                                  \
    X(Rem)                                  \
    X(Random)                               \
    X(NonMathFunction)

// https://www.w3.org/TR/css-values-4/#calculation-tree
class CalculationNode : public RefCounted<CalculationNode> {
public:
    // NOTE: Currently, any value with a `var()` or `attr()` function in it is always an
    //       UnresolvedStyleValue so we do not have to implement them as CalculationNodes.

    enum class Type {
#define ENUMERATE_TYPE(name) name,
        ENUMERATE_CALCULATION_NODE_TYPES(ENUMERATE_TYPE)
#undef ENUMERATE_TYPE
    };

    template<typename T>
    bool fast_is() const = delete;

    using NumericValue = CalculatedStyleValue::CalculationResult::Value;

    virtual ~CalculationNode();

    static NonnullRefPtr<CalculationNode const> from_style_value(NonnullRefPtr<StyleValue const> const&, CalculationContext const&);

    Type type() const { return m_type; }

    // https://www.w3.org/TR/css-values-4/#calculation-tree-operator-nodes
    bool is_operator_node() const
    {
        return is_calc_operator_node() || is_math_function_node();
    }

    bool is_math_function_node() const
    {
        switch (m_type) {
        case Type::Min:
        case Type::Max:
        case Type::Clamp:
        case Type::Abs:
        case Type::Sign:
        case Type::Sin:
        case Type::Cos:
        case Type::Tan:
        case Type::Asin:
        case Type::Acos:
        case Type::Atan:
        case Type::Atan2:
        case Type::Pow:
        case Type::Sqrt:
        case Type::Hypot:
        case Type::Log:
        case Type::Exp:
        case Type::Round:
        case Type::Mod:
        case Type::Rem:
        case Type::Random:
            return true;

        default:
            return false;
        }
    }

    // https://www.w3.org/TR/css-values-4/#calculation-tree-calc-operator-nodes
    bool is_calc_operator_node() const
    {
        return first_is_one_of(m_type, Type::Sum, Type::Product, Type::Negate, Type::Invert);
    }

    StringView name() const;
    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const = 0;

    Optional<NumericType> const& numeric_type() const { return m_numeric_type; }
    virtual bool contains_percentage() const = 0;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const = 0;
    // Step 4 of simpliRfy_a_calculation_tree(). Only valid for math-function nodes.
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const { VERIFY_NOT_REACHED(); }

    virtual void dump(StringBuilder&, int indent) const = 0;
    virtual bool equals(CalculationNode const&) const = 0;
    virtual bool is_computationally_independent() const = 0;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const { return nullptr; }

protected:
    CalculationNode(Type, Optional<NumericType>);

private:
    Type m_type;
    Optional<NumericType> m_numeric_type;
};

enum class NonFiniteValue {
    Infinity,
    NegativeInfinity,
    NaN,
};

class NumericCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<NumericCalculationNode const> create(NumericValue, CalculationContext const&);
    static RefPtr<NumericCalculationNode const> from_keyword(Keyword, CalculationContext const&);
    ~NumericCalculationNode();

    virtual bool contains_percentage() const override;
    bool is_in_canonical_unit() const;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override { return *this; }

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return {}; }
    NumericValue const& value() const { return m_value; }
    void serialize_value(StringBuilder&) const;
    String value_to_string() const;

    Optional<NonFiniteValue> infinite_or_nan_value() const;
    bool is_negative() const;
    NonnullRefPtr<NumericCalculationNode const> negated(CalculationContext const&) const;

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    NumericCalculationNode(NumericValue, NumericType);
    NumericValue m_value;
};

class SumCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<SumCalculationNode const> create(Vector<NonnullRefPtr<CalculationNode const>>);
    ~SumCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return m_values; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    SumCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
    Vector<NonnullRefPtr<CalculationNode const>> m_values;
};

class ProductCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<ProductCalculationNode const> create(Vector<NonnullRefPtr<CalculationNode const>>);
    ~ProductCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return m_values; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    ProductCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
    Vector<NonnullRefPtr<CalculationNode const>> m_values;
};

class NegateCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<NegateCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~NegateCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }
    CalculationNode const& child() const { return m_value; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    explicit NegateCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class InvertCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<InvertCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~InvertCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }
    CalculationNode const& child() const { return m_value; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    InvertCalculationNode(NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class MinCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<MinCalculationNode const> create(Vector<NonnullRefPtr<CalculationNode const>>);
    ~MinCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return m_values; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    MinCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
    Vector<NonnullRefPtr<CalculationNode const>> m_values;
};

class MaxCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<MaxCalculationNode const> create(Vector<NonnullRefPtr<CalculationNode const>>);
    ~MaxCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return m_values; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    MaxCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
    Vector<NonnullRefPtr<CalculationNode const>> m_values;
};

class ClampCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<ClampCalculationNode const> create(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    ~ClampCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_min_value, m_center_value, m_max_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;
    virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;

private:
    ClampCalculationNode(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
    NonnullRefPtr<CalculationNode const> m_min_value;
    NonnullRefPtr<CalculationNode const> m_center_value;
    NonnullRefPtr<CalculationNode const> m_max_value;
};

class AbsCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<AbsCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~AbsCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit AbsCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class SignCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<SignCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~SignCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit SignCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class SinCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<SinCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~SinCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit SinCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class CosCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<CosCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~CosCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit CosCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class TanCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<TanCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~TanCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit TanCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class AsinCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<AsinCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~AsinCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit AsinCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class AcosCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<AcosCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~AcosCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit AcosCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class AtanCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<AtanCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~AtanCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit AtanCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class Atan2CalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<Atan2CalculationNode const> create(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    ~Atan2CalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_y, m_x } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    Atan2CalculationNode(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_y;
    NonnullRefPtr<CalculationNode const> m_x;
};

class PowCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<PowCalculationNode const> create(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    ~PowCalculationNode();

    virtual bool contains_percentage() const override { return false; }
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_x, m_y } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    PowCalculationNode(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_x;
    NonnullRefPtr<CalculationNode const> m_y;
};

class SqrtCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<SqrtCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~SqrtCalculationNode();

    virtual bool contains_percentage() const override { return false; }
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit SqrtCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class HypotCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<HypotCalculationNode const> create(Vector<NonnullRefPtr<CalculationNode const>>);
    ~HypotCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return m_values; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;

private:
    HypotCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
    Vector<NonnullRefPtr<CalculationNode const>> m_values;
    virtual bool is_computationally_independent() const override;
};

class LogCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<LogCalculationNode const> create(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    ~LogCalculationNode();

    virtual bool contains_percentage() const override { return false; }
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_x, m_y } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    LogCalculationNode(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_x;
    NonnullRefPtr<CalculationNode const> m_y;
};

class ExpCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<ExpCalculationNode const> create(NonnullRefPtr<CalculationNode const>);
    ~ExpCalculationNode();

    virtual bool contains_percentage() const override { return false; }
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_value } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    explicit ExpCalculationNode(NonnullRefPtr<CalculationNode const>);
    NonnullRefPtr<CalculationNode const> m_value;
};

class RoundCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<RoundCalculationNode const> create(RoundingStrategy, NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    ~RoundCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    // NOTE: This excludes the rounding strategy!
    RoundingStrategy rounding_strategy() const { return m_strategy; }
    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_x, m_y } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    RoundCalculationNode(RoundingStrategy, NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
    RoundingStrategy m_strategy;
    NonnullRefPtr<CalculationNode const> m_x;
    NonnullRefPtr<CalculationNode const> m_y;
};

class ModCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<ModCalculationNode const> create(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    ~ModCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_x, m_y } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    ModCalculationNode(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
    NonnullRefPtr<CalculationNode const> m_x;
    NonnullRefPtr<CalculationNode const> m_y;
};

class RandomCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<RandomCalculationNode const> create(NonnullRefPtr<RandomValueSharingStyleValue const>, NonnullRefPtr<CalculationNode const> minimum, NonnullRefPtr<CalculationNode const> maximum, RefPtr<CalculationNode const> step);
    ~RandomCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    // NOTE: We don't return children here as serialization is handled ad-hoc
    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return {}; }

    void serialize(StringBuilder&, CalculationContext const&, SerializationMode) const;
    String to_string(CalculationContext const&, SerializationMode serialization_mode) const;

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    RandomCalculationNode(NonnullRefPtr<RandomValueSharingStyleValue const>, NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, RefPtr<CalculationNode const>, Optional<NumericType>);
    ValueComparingNonnullRefPtr<RandomValueSharingStyleValue const> m_random_value_sharing;
    ValueComparingNonnullRefPtr<CalculationNode const> m_minimum;
    ValueComparingNonnullRefPtr<CalculationNode const> m_maximum;
    ValueComparingRefPtr<CalculationNode const> m_step;
};

class RemCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<RemCalculationNode const> create(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);
    ~RemCalculationNode();

    virtual bool contains_percentage() const override;
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
    virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;

    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return { { m_x, m_y } }; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

private:
    RemCalculationNode(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
    NonnullRefPtr<CalculationNode const> m_x;
    NonnullRefPtr<CalculationNode const> m_y;
};

class NonMathFunctionCalculationNode final : public CalculationNode {
public:
    static NonnullRefPtr<NonMathFunctionCalculationNode const> create(AbstractNonMathCalcFunctionStyleValue const&, NumericType);
    ~NonMathFunctionCalculationNode();

    virtual bool contains_percentage() const override { return false; }
    virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override { return *this; }
    virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return {}; }

    virtual void dump(StringBuilder&, int indent) const override;
    virtual bool equals(CalculationNode const&) const override;
    virtual bool is_computationally_independent() const override;

    ValueComparingNonnullRefPtr<AbstractNonMathCalcFunctionStyleValue const> function() const { return m_function; }

private:
    NonMathFunctionCalculationNode(AbstractNonMathCalcFunctionStyleValue const& function, NumericType);
    ValueComparingNonnullRefPtr<AbstractNonMathCalcFunctionStyleValue const> m_function;
};

#define ENUMERATE_TYPE(name) \
    template<>               \
    inline bool CalculationNode::fast_is<name##CalculationNode>() const { return type() == Type::name; }
ENUMERATE_CALCULATION_NODE_TYPES(ENUMERATE_TYPE)
#undef ENUMERATE_TYPE

// https://drafts.csswg.org/css-values-4/#calc-simplification
NonnullRefPtr<CalculationNode const> simplify_a_calculation_tree(CalculationNode const& root, CalculationContext const& context, CalculationResolutionContext const& resolution_context);

}
