/**
* This file is part of the "libunicode" project
*   Copyright (c) 2020-2021 Christian Parpart <christian@parpart.family>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <libunicode/ucd_enums.h>

#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <utility>

namespace unicode
{

Plane plane(char32_t codepoint) noexcept;

bool contains(Core_Property prop, char32_t codepoint) noexcept;

bool contains(General_Category generalCategory, char32_t codepoint) noexcept;

// Disabling clang-format to avoid single-line folding implementations.
// clang-format off
namespace general_category
{
    General_Category get(char32_t value) noexcept;

    inline bool is_close_punctuation(char32_t codepoint) noexcept
    {
        return contains(General_Category::Close_Punctuation, codepoint);
    }

    inline bool is_connector_punctuation(char32_t codepoint) noexcept
    {
        return contains(General_Category::Connector_Punctuation, codepoint);
    }

    inline bool is_control(char32_t codepoint) noexcept
    {
        return contains(General_Category::Control, codepoint);
    }

    inline bool is_currency_symbol(char32_t codepoint) noexcept
    {
        return contains(General_Category::Currency_Symbol, codepoint);
    }

    inline bool is_dash_punctuation(char32_t codepoint) noexcept
    {
        return contains(General_Category::Dash_Punctuation, codepoint);
    }

    inline bool is_decimal_number(char32_t codepoint) noexcept
    {
        return contains(General_Category::Decimal_Number, codepoint);
    }

    inline bool is_enclosing_mark(char32_t codepoint) noexcept
    {
        return contains(General_Category::Enclosing_Mark, codepoint);
    }

    inline bool is_final_punctuation(char32_t codepoint) noexcept
    {
        return contains(General_Category::Final_Punctuation, codepoint);
    }

    inline bool is_format(char32_t codepoint) noexcept
    {
        return contains(General_Category::Format, codepoint);
    }

    inline bool is_initial_punctuation(char32_t codepoint) noexcept
    {
        return contains(General_Category::Initial_Punctuation, codepoint);
    }

    inline bool is_letter_number(char32_t codepoint) noexcept
    {
        return contains(General_Category::Letter_Number, codepoint);
    }

    inline bool is_line_separator(char32_t codepoint) noexcept
    {
        return contains(General_Category::Line_Separator, codepoint);
    }

    inline bool is_lowercase_letter(char32_t codepoint) noexcept
    {
        return contains(General_Category::Lowercase_Letter, codepoint);
    }

    inline bool is_math_symbol(char32_t codepoint) noexcept
    {
        return contains(General_Category::Math_Symbol, codepoint);
    }

    inline bool is_modifier_letter(char32_t codepoint) noexcept
    {
        return contains(General_Category::Modifier_Letter, codepoint);
    }

    inline bool is_modifier_symbol(char32_t codepoint) noexcept
    {
        return contains(General_Category::Modifier_Symbol, codepoint);
    }

    inline bool is_nonspacing_mark(char32_t codepoint) noexcept
    {
        return contains(General_Category::Nonspacing_Mark, codepoint);
    }

    inline bool is_open_punctuation(char32_t codepoint) noexcept
    {
        return contains(General_Category::Open_Punctuation, codepoint);
    }

    inline bool is_other_letter(char32_t codepoint) noexcept
    {
        return contains(General_Category::Other_Letter, codepoint);
    }

    inline bool is_other_number(char32_t codepoint) noexcept
    {
        return contains(General_Category::Other_Number, codepoint);
    }

    inline bool is_other_punctuation(char32_t codepoint) noexcept
    {
        return contains(General_Category::Other_Punctuation, codepoint);
    }

    inline bool is_other_symbol(char32_t codepoint) noexcept
    {
        return contains(General_Category::Other_Symbol, codepoint);
    }

    inline bool is_paragraph_separator(char32_t codepoint) noexcept
    {
        return contains(General_Category::Paragraph_Separator, codepoint);
    }

    inline bool is_private_use(char32_t codepoint) noexcept
    {
        return contains(General_Category::Private_Use, codepoint);
    }

    inline bool is_space_separator(char32_t codepoint) noexcept
    {
        return contains(General_Category::Space_Separator, codepoint);
    }

    inline bool is_spacing_mark(char32_t codepoint) noexcept
    {
        return contains(General_Category::Spacing_Mark, codepoint);
    }

    inline bool is_surrogate(char32_t codepoint) noexcept
    {
        return contains(General_Category::Surrogate, codepoint);
    }

    inline bool is_titlecase_letter(char32_t codepoint) noexcept
    {
        return contains(General_Category::Titlecase_Letter, codepoint);
    }

    inline bool is_unassigned(char32_t codepoint) noexcept
    {
        return contains(General_Category::Unassigned, codepoint);
    }

    inline bool is_uppercase_letter(char32_t codepoint) noexcept
    {
        return contains(General_Category::Uppercase_Letter, codepoint);
    }

} // namespace general_category
// clang-format on

Script script(char32_t codepoint) noexcept;

std::optional<std::span<Script const>> script_extensions(char32_t codepoint) noexcept;

Block block(char32_t codepoint) noexcept;

Grapheme_Cluster_Break grapheme_cluster_break(char32_t codepoint) noexcept;

inline std::string to_string(EastAsianWidth value)
{
    switch (value)
    {
        case EastAsianWidth::Ambiguous: return "Ambiguous";
        case EastAsianWidth::FullWidth: return "FullWidth";
        case EastAsianWidth::HalfWidth: return "HalfWidth";
        case EastAsianWidth::Neutral: return "Neutral";
        case EastAsianWidth::Narrow: return "Narrow";
        case EastAsianWidth::Wide: return "Wide";
        case EastAsianWidth::Unspecified: return "Unspecified";
    }
    return "Unknown";
}

EastAsianWidth east_asian_width(char32_t codepoint) noexcept;

bool is_emoji(char32_t codepoint) noexcept;
bool is_emoji_component(char32_t codepoint) noexcept;
bool is_emoji_modifier(char32_t codepoint) noexcept;
bool is_emoji_modifier_base(char32_t codepoint) noexcept;
bool is_emoji_presentation(char32_t codepoint) noexcept;
bool is_extended_pictographic(char32_t codepoint) noexcept;

/// Returns true if the codepoint has the Bidi_Mirrored property.
[[nodiscard]] bool is_mirrored(char32_t codepoint) noexcept;

/// Returns the Bidi_Mirroring_Glyph for a codepoint.
/// If no mirroring glyph exists, returns the input codepoint unchanged.
[[nodiscard]] char32_t bidi_mirroring_glyph(char32_t codepoint) noexcept;

/// Parses a Unicode version string (e.g., "1.1", "17.0") to an Age enum value.
/// @param value The version string from DerivedAge.txt.
/// @return The corresponding Age enum value, or std::nullopt if not recognized.
[[nodiscard]] std::optional<Age> make_age(std::string_view value) noexcept;

} // namespace unicode
