/*
 * Copyright (C) 2013 Google Inc. All rights reserved.
 * Copyright (C) 2013, 2014 Igalia S.L.
 * Copyright (C) 2025 Samuel Weinig <sam@webkit.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include <WebCore/StylePrimitiveNumericOrKeyword.h>

namespace WebCore {

namespace CSS {
struct GridTrackBreadth;
}

class CSSKeywordValue;

namespace Style {

// FIXME: Make PrimitiveNumericOrKeyword support additional numeric types in addition to the <length-percentage> one and then replace GridTrackBreadth with that.

struct GridTrackBreadthLength : PrimitiveNumericOrKeyword<LengthPercentage<CSS::Nonnegative>, CSS::Keyword::MinContent, CSS::Keyword::MaxContent, CSS::Keyword::Auto> {
    using Base::Base;

    ALWAYS_INLINE bool isMinContent() const { return holdsAlternative<CSS::Keyword::MinContent>(); }
    ALWAYS_INLINE bool isMaxContent() const { return holdsAlternative<CSS::Keyword::MaxContent>(); }
    ALWAYS_INLINE bool isAuto() const { return holdsAlternative<CSS::Keyword::Auto>(); }
};

// <track-breadth> = <length-percentage [0,inf]> | <flex [0,inf]> | min-content | max-content | auto
// https://www.w3.org/TR/css-grid-1/#typedef-track-breadth
class GridTrackBreadth {
public:
    using Fixed = GridTrackBreadthLength::Fixed;
    using Percentage = GridTrackBreadthLength::Percentage;
    using Calc = GridTrackBreadthLength::Calc;
    using Flex = Style::Flex<CSS::Nonnegative>;

    GridTrackBreadth(Fixed fixed)
        : m_length(fixed)
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(Percentage percent)
        : m_length(percent)
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(GridTrackBreadthLength&& length)
        : m_length(WTF::move(length))
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(Flex flex)
        : m_length(CSS::Literals::px(0))
        , m_flex(flex)
        , m_type(GridTrackBreadthType::Flex)
    {
    }

    GridTrackBreadth(CSS::Keyword::MinContent keyword)
        : m_length(keyword)
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(CSS::Keyword::MaxContent keyword)
        : m_length(keyword)
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(CSS::Keyword::Auto keyword)
        : m_length(keyword)
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(CSS::ValueLiteral<CSS::LengthUnit::Px> literal)
        : m_length(literal)
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(CSS::ValueLiteral<CSS::PercentageUnit::Percentage> literal)
        : m_length(literal)
        , m_flex(CSS::Literals::fr(0))
        , m_type(GridTrackBreadthType::Length)
    {
    }

    GridTrackBreadth(CSS::ValueLiteral<CSS::FlexUnit::Fr> literal)
        : m_length(CSS::Literals::px(0))
        , m_flex(literal)
        , m_type(GridTrackBreadthType::Flex)
    {
    }

    bool isLength() const { return m_type == GridTrackBreadthType::Length; }
    bool isFlex() const { return m_type == GridTrackBreadthType::Flex; }

    const GridTrackBreadthLength& length() const LIFETIME_BOUND { ASSERT(isLength()); return m_length; }
    Flex flex() const { ASSERT(isFlex()); return m_flex; }

    bool isPercentOrCalculated() const { return m_type == GridTrackBreadthType::Length && m_length.isPercentOrCalculated(); }
    bool isContentSized() const { return m_type == GridTrackBreadthType::Length && (m_length.isAuto() || m_length.isMinContent() || m_length.isMaxContent()); }
    bool isAuto() const { return m_type == GridTrackBreadthType::Length && m_length.isAuto(); }

    template<typename... F> decltype(auto) switchOn(F&&... f) const
    {
        auto visitor = WTF::makeVisitor(std::forward<F>(f)...);

        if (isFlex())
            return visitor(m_flex);
        return WTF::switchOn(m_length, [&](const auto& value) { return visitor(value); });
    }

    template<typename... F> decltype(auto) switchOnUsingNumeric(F&&... f) const
    {
        auto visitor = WTF::makeVisitor(std::forward<F>(f)...);

        if (isFlex())
            return visitor(m_flex);
        return m_length.switchOnUsingNumeric([&](const auto& value) { return visitor(value); });
    }

    bool operator==(const GridTrackBreadth&) const = default;

private:
    GridTrackBreadthLength m_length;
    Flex m_flex;

    enum class GridTrackBreadthType : bool { Length, Flex };
    GridTrackBreadthType m_type;
};

// MARK: - Conversion

template<> struct ToCSS<GridTrackBreadth> {
    auto operator()(const GridTrackBreadth&, const Style::ComputedStyle&) -> CSS::GridTrackBreadth;
};
template<> struct ToStyle<CSS::GridTrackBreadth> {
    auto operator()(const CSS::GridTrackBreadth&, const BuilderState&) -> GridTrackBreadth;
};

template<> struct CSSValueConversion<GridTrackBreadth> {
    auto operator()(BuilderState&, const CSSPrimitiveValue&) -> GridTrackBreadth;
    auto operator()(BuilderState&, const CSSKeywordValue&) -> GridTrackBreadth;
};

// MARK: - Blending

template<> struct Blending<GridTrackBreadth> {
    auto blend(const GridTrackBreadth&, const GridTrackBreadth&, const BlendingContext&) -> GridTrackBreadth;
};

} // namespace Style
} // namespace WebCore

DEFINE_VARIANT_LIKE_CONFORMANCE(WebCore::Style::GridTrackBreadthLength)
DEFINE_VARIANT_LIKE_CONFORMANCE(WebCore::Style::GridTrackBreadth)
