# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021 Intel Corporation

"""Helpers for strict type checking."""

import typing as T

from .. import compilers
from ..coredata import UserFeatureOption
from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo
from ..mesonlib import FileMode, MachineChoice


def _language_validator(l: T.List[str]) -> T.Optional[str]:
    """Validate language keyword argument.

    Particularly for functions like `add_compiler()`, and `add_*_args()`
    """
    diff = {a.lower() for a in l}.difference(compilers.all_languages)
    if diff:
        return f'unknown languages: {", ".join(diff)}'
    return None


def _install_mode_validator(mode: T.List[T.Union[str, bool, int]]) -> T.Optional[str]:
    """Validate the `install_mode` keyword argument.

    This is a rather odd thing, it's a scalar, or an array of 3 values in the form:
    [(str | False), (str | int | False) = False, (str | int | False) = False]
    Where the second and third arguments are not required, and are considered to
    default to False.
    """
    if not mode:
        return None
    if True in mode:
        return 'can only be a string or false, not true'
    if len(mode) > 3:
        return 'may have at most 3 elements'

    perms = mode[0]
    if not isinstance(perms, (str, bool)):
        return 'permissions part must be a string or false'

    if isinstance(perms, str):
        if not len(perms) == 9:
            return (f'permissions string must be exactly 9 characters, got "{len(perms)}" '
                   'in the form rwxr-xr-x')
        for i in [0, 3, 6]:
            if perms[i] not in {'-', 'r'}:
                return f'bit {i} must be "-" or "r", not {perms[i]}'
        for i in [1, 4, 7]:
            if perms[i] not in {'-', 'w'}:
                return f'bit {i} must be "-" or "w", not {perms[i]}'
        for i in [2, 5]:
            if perms[i] not in {'-', 'x', 's', 'S'}:
                return f'bit {i} must be "-", "s", "S", or "x", not {perms[i]}'
        if perms[8] not in {'-', 'x', 't', 'T'}:
            return f'bit 8 must be "-", "t", "T", or "x", not {perms[8]}'

        if len(mode) >= 2 and not isinstance(mode[1], (int, str, bool)):
            return 'second componenent must be a string, number, or False if provided'
        if len(mode) >= 3 and not isinstance(mode[2], (int, str, bool)):
            return 'third componenent must be a string, number, or False if provided'

    return None


def _install_mode_convertor(mode: T.Optional[T.List[T.Union[str, bool, int]]]) -> FileMode:
    """Convert the DSL form of the `install_mode` keyword arugment to `FileMode`

    This is not required, and if not required returns None

    TODO: It's not clear to me why this needs to be None and not just return an
    emtpy FileMode.
    """
    # this has already been validated by the validator
    return FileMode(*[m if isinstance(m, str) else None for m in mode])


NATIVE_KW = KwargInfo(
    'native', bool,
    default=False,
    convertor=lambda n: MachineChoice.BUILD if n else MachineChoice.HOST)

LANGUAGE_KW = KwargInfo(
    'language', ContainerTypeInfo(list, str, allow_empty=False),
    listify=True,
    required=True,
    validator=_language_validator,
    convertor=lambda x: [i.lower() for i in x])

INSTALL_MODE_KW = KwargInfo(
    'install_mode',
    ContainerTypeInfo(list, (str, bool, int)),
    listify=True,
    default=[],
    validator=_install_mode_validator,
    convertor=_install_mode_convertor,
)

REQUIRED_KW = KwargInfo(
    'required',
    (bool, UserFeatureOption),
    default=True,
    # TODO: extract_required_kwarg could be converted to a convertor
)

@@ -3306,24 +3306,68 @@
 TALL_MODE_KW
+: KwargInfo%5BT.List%5BT.Union%5Bstr, bool, int%5D%5D%5D
  = KwargInfo
@@ -3561,16 +3561,61 @@
 UIRED_KW
+: KwargInfo%5BT.Union%5Bbool, UserFeatureOption%5D%5D
  = Kwarg

Add some type annotations