# This file is a part of Groebner.jl. License is GNU GPL v2.

# Handling keyword arguments for functions in the interface.

const INT_INF = typemax(Int)

#! format: off
# Syntax formatting is disabled for the next several lines.
#
# Maps a function name from the interface to a set of supported keyword
# arguments with their corresponding default values.
const _supported_kw_args = (
    dimension = (
        monoms       = :auto,
    ),
    groebner = (
        reduced      = true,
        ordering     = InputOrdering(),
        certify      = false,
        linalg       = :auto,
        monoms       = :auto,
        arithmetic   = :auto,
        seed         = 42,
        selection    = :auto,
        modular      = :auto,
        tasks        = :auto,
        homogenize   = :auto,
        changematrix = false,
        _composite   = 4,
        _generic     = false,
        _use_divmask = true,
    ),
    groebner_apply! = (
        seed        = 42,
        ordering    = InputOrdering(),
        monoms      = :auto,
        arithmetic  = :auto,
        tasks       = :auto,
        _generic     = false,
    ),
    groebner_learn = (
        seed        = 42,
        ordering    = InputOrdering(),
        monoms      = :auto,
        arithmetic  = :auto,
        homogenize  = :auto,
        tasks       = :auto,
        _generic     = false,
    ),
    groebner_with_change_matrix = (
        reduced      = true,
        ordering     = InputOrdering(),
        certify      = false,
        linalg       = :auto,
        monoms       = :auto,
        arithmetic   = :auto,
        seed         = 42,
        selection    = :auto,
        modular      = :auto,
        tasks        = :auto,
        homogenize   = :auto,
        changematrix = true,
        _composite   = 4,
    ),
    isgroebner = (
        ordering    = InputOrdering(),
        certify     = false,
        seed        = 42,
        monoms      = :auto,
        linalg      = :auto,
    ),
    leading_term = (
        ordering    = InputOrdering(),
        monoms      = :auto,
    ),
    leading_ideal = (
        ordering    = InputOrdering(),
        monoms      = :auto,
    ),
    normalform = (
        check       = false,
        ordering    = InputOrdering(),
        monoms      = :auto,
    ),
    quotient_basis = (
        ordering    = InputOrdering(),
        monoms      = :auto,
    ),
)
#! format: on

mutable struct KeywordArguments
    function_id::Symbol
    reduced::Bool
    ordering::Any
    certify::Bool
    linalg::Symbol
    tasks::Any
    monoms::Symbol
    arithmetic::Symbol
    seed::Int
    selection::Symbol
    modular::Symbol
    _composite::Int
    check::Bool
    homogenize::Symbol
    _use_divmask::Bool
    changematrix::Bool
    _generic::Bool
end

function KeywordArguments(function_id::Symbol, kws)
    @assert haskey(_supported_kw_args, function_id)
    default_kw_args = _supported_kw_args[function_id]
    for (key, _) in kws
        if !haskey(default_kw_args, key)
            error_msg = join(sort(map(string, collect(keys(default_kw_args)))), ", ")
            throw(AssertionError("""
            Keyword \"$key\" is not supported by Groebner.$(function_id).
            Supported keyword arguments for Groebner.$(function_id) are:\n$error_msg"""))
        end
    end

    reduced = get(kws, :reduced, get(default_kw_args, :reduced, true))
    ordering = get(kws, :ordering, get(default_kw_args, :ordering, InputOrdering()))
    certify = get(kws, :certify, get(default_kw_args, :certify, false))

    linalg = get(kws, :linalg, get(default_kw_args, :linalg, :auto))
    @assert linalg in
            (:auto, :randomized, :deterministic, :experimental_1, :experimental_2, :experimental_3) """
                                                                                                  Not recognized linear algebra option: $linalg
                                                                                                  Possible choices for keyword "linalg" are:
                                                                                                  `:auto`, `:randomized`, `:deterministic`"""

    tasks = get(kws, :tasks, get(default_kw_args, :tasks, :auto))
    @assert tasks === :auto || (tasks isa Integer && tasks >= 1) """
    Not recognized tasks option: $tasks
    Possible choices for keyword "tasks" are:
    `:auto`, `1`, `2`, `3`, ... (any positive integer)"""

    monoms = get(kws, :monoms, get(default_kw_args, :monoms, :dense))
    @assert monoms in (:auto, :dense, :packed, :fixed, :fixed2, :fixednodeg, :nibble, :nibblenodeg) """
    Not recognized monomial representation: $monoms
    Possible choices for keyword "monoms" are:
    `:auto`, `:dense`, `:packed`, `:nibble`"""

    arithmetic = get(kws, :arithmetic, get(default_kw_args, :arithmetic, :auto))
    @assert arithmetic in (:auto, :delayed, :signed, :basic) """
    Not recognized arithmetic: $arithmetic
    Possible choices for keyword "arithmetic" are:
    `:auto`, `:delayed`, `:signed`, `:basic`"""

    seed = get(kws, :seed, get(default_kw_args, :seed, 42))

    modular = get(kws, :modular, get(default_kw_args, :modular, :auto))
    @assert modular in (:auto, :classic_modular, :learn_and_apply) """
    Not recognized modular strategy: $modular
    Possible choices for keyword "modular" are:
    `:auto`, `:classic_modular`, `:learn_and_apply`"""

    _composite = get(kws, :_composite, get(default_kw_args, :_composite, 4))
    _use_divmask = get(kws, :_use_divmask, get(default_kw_args, :_use_divmask, true))

    selection = get(kws, :selection, get(default_kw_args, :selection, :auto))
    @assert selection in (:auto, :normal, :sugar, :be_divided_and_perish)

    check = get(kws, :check, get(default_kw_args, :check, true))
    homogenize = get(kws, :homogenize, get(default_kw_args, :homogenize, :auto))
    @assert homogenize in (:auto, :no, :yes) """
    Not recognized homogenization strategy: $homogenize
    Possible choices for keyword "homogenize" are:
    `:auto`, `:no`, `:yes`"""

    changematrix = get(kws, :changematrix, get(default_kw_args, :changematrix, false))

    _generic = get(kws, :_generic, get(default_kw_args, :_generic, false))

    KeywordArguments(
        function_id,
        reduced,
        ordering,
        certify,
        linalg,
        tasks,
        monoms,
        arithmetic,
        seed,
        selection,
        modular,
        _composite,
        check,
        homogenize,
        _use_divmask,
        changematrix,
        _generic
    )
end
