# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021-2025 Intel Corporation
# Copyright © 2021-2025 Intel Corporation
from __future__ import annotations

"""Keyword Argument type annotations."""

import typing as T

from typing_extensions import TypedDict, Literal, Protocol, NotRequired

from .. import build
from .. import options
from ..compilers import Compiler
from ..compilers.compilers import Language
from ..dependencies.base import Dependency, DependencyMethods, IncludeType
from ..interpreterbase import Feature
from ..mesonlib import EnvironmentVariables, MachineChoice, File, FileMode, FileOrString
from ..options import OptionKey
from ..modules.cmake import CMakeSubprojectOptions
from ..programs import Program, ExternalProgram
from .type_checking import PkgConfigDefineType, SourcesVarargsType

TargetDepends = T.Union[build.CustomTarget, build.CustomTargetIndex, build.BuildTarget, build.GeneratedList, Program]
CustomTargetInputs = T.Union[str, build.BuildTarget, build.GeneratedTypes,
                             build.ExtractedObjects, Program, File]
BuildTargetObjects = T.Union[str, File, build.ExtractedObjects, build.GeneratedTypes]
RustAbi = Literal['rust', 'c']

class NativeKW(TypedDict):

    native: MachineChoice


class FuncAddProjectArgs(TypedDict):

    """Keyword Arguments for the add_*_arguments family of arguments.

    including `add_global_arguments`, `add_project_arguments`, and their
    link variants

    Because of the use of a convertor function, we get the native keyword as
    a MachineChoice instance already.
    """

    native: MachineChoice
    language: T.List[Language]


class BaseTest(TypedDict):

    """Shared base for the Rust module."""

    should_fail: T.Optional[bool]
    expected_fail: T.Optional[bool]
    expected_exitcode: T.Optional[int]
    timeout: int
    workdir: T.Optional[str]
    priority: int
    env: EnvironmentVariables
    depends: T.List[TargetDepends]
    suite: T.List[str]
    verbose: bool


class FuncBenchmark(BaseTest):

    """Keyword arguments used by `benchmark` and `test`, but not Rust"""

    # Needs complex refactor for rust test to go in BaseTest
    args: T.List[build.CommandTypes]
    protocol: Literal['exitcode', 'tap', 'gtest', 'rust']

class FuncTest(FuncBenchmark):

    """Keyword Arguments for `test` but not `benchmark`"""

    is_parallel: bool


class ExtractRequired(TypedDict):

    """Keyword Arguments consumed by the `extract_required_kwargs` function.

    Any function that uses the `required` keyword argument which accepts either
    a boolean or a feature option should inherit its arguments from this class.
    """

    required: T.Union[bool, Feature]


class ExtractSearchDirs(TypedDict):

    """Keyword arguments consumed by the `extract_search_dirs` function.

    See the not in `ExtractRequired`
    """

    dirs: T.List[str]


class FuncGenerator(TypedDict):

    """Keyword rguments for the generator function."""

    arguments: T.List[str]
    output: T.List[str]
    depfile: T.Optional[str]
    capture:  bool
    depends: T.List[TargetDepends]


class GeneratorProcess(TypedDict):

    """Keyword Arguments for generator.process."""

    preserve_path_from: T.Optional[str]
    extra_args: T.List[str]
    env: EnvironmentVariables
    depends: T.List[build.GeneratedTypes]

class DependencyMethodPartialDependency(TypedDict):

    """ Keyword Arguments for the dep.partial_dependency methods """

    compile_args: bool
    link_args: bool
    links: bool
    includes: bool
    sources: bool

class BuildTargeMethodExtractAllObjects(TypedDict):
    recursive: bool

class FuncInstallSubdir(TypedDict):

    install_dir: str
    install_tag: T.Optional[str]
    strip_directory: bool
    exclude_files: T.List[str]
    exclude_directories: T.List[str]
    install_mode: FileMode
    follow_symlinks: T.Optional[bool]


class FuncInstallData(TypedDict):

    install_dir: str
    sources: T.List[FileOrString]
    rename: T.List[str]
    install_mode: FileMode
    install_tag: T.Optional[str]
    follow_symlinks: T.Optional[bool]
    preserve_path: bool


class FuncInstallEmptyDir(TypedDict):

    install_mode: FileMode
    install_tag: T.Optional[str]


class FuncInstallHeaders(TypedDict):

    install_dir: T.Optional[str]
    install_mode: FileMode
    subdir: T.Optional[str]
    follow_symlinks: T.Optional[bool]
    install_tag: T.Optional[str]
    preserve_path: bool


class FuncInstallMan(TypedDict):

    install_dir: T.Optional[str]
    install_mode: FileMode
    install_tag: T.Optional[str]
    locale: T.Optional[str]


class FuncInstallSymlink(TypedDict):

    install_dir: T.Optional[str]
    install_tag: T.Optional[str]
    pointing_to: str


class FuncImportModule(ExtractRequired):

    disabler: bool


class FuncIncludeDirectories(TypedDict):

    is_system: bool

class FuncAddLanguages(ExtractRequired):

    native: MachineChoice | None

class RunTarget(TypedDict):

    command: T.List[build.CommandTypes]
    depends: T.List[TargetDepends]
    env: EnvironmentVariables


class CustomTarget(TypedDict):

    build_always: bool
    build_always_stale: T.Optional[bool]
    build_by_default: T.Optional[bool]
    build_subdir: str
    capture: bool
    command: T.List[build.CommandTypes]
    console: bool
    depend_files: T.List[FileOrString]
    depends: T.List[TargetDepends]
    depfile: T.Optional[str]
    env: EnvironmentVariables
    feed: bool
    input: T.List[CustomTargetInputs]
    install: bool
    install_dir: T.List[T.Union[str, T.Literal[False]]]
    install_mode: FileMode
    install_tag: T.List[T.Optional[str]]
    output: T.List[str]

class AddTestSetup(TypedDict):

    exe_wrapper: T.List[T.Union[str, ExternalProgram]]
    gdb: bool
    timeout_multiplier: int
    is_default: bool
    exclude_suites: T.List[str]
    env: EnvironmentVariables


class Project(TypedDict):

    version: FileOrString
    meson_version: T.Optional[str]
    default_options: options.OptionDict
    license: T.List[str]
    license_files: T.List[str]
    subproject_dir: str


class _FoundProto(Protocol):

    """Protocol for subdir arguments.

    This allows us to define any object that has a found(self) -> bool method
    """

    def found(self) -> bool: ...


class Subdir(TypedDict):

    if_found: T.List[_FoundProto]


class Summary(TypedDict):

    section: str
    bool_yn: bool
    list_sep: T.Optional[str]


class FindProgram(ExtractRequired, ExtractSearchDirs):

    default_options: T.Dict[OptionKey, options.ElementaryOptionValues]
    native: MachineChoice
    version: T.List[str]
    version_argument: str


class RunCommand(TypedDict):

    check: bool
    capture: T.Optional[bool]
    console: T.Optional[bool]
    env: EnvironmentVariables


class FeatureOptionRequire(TypedDict):

    error_message: T.Optional[str]


class DependencyPkgConfigVar(TypedDict):

    default: T.Optional[str]
    define_variable: PkgConfigDefineType


class DependencyGetVariable(TypedDict):

    cmake: T.Optional[str]
    pkgconfig: T.Optional[str]
    configtool: T.Optional[str]
    internal: T.Optional[str]
    system: T.Optional[str]
    default_value: T.Optional[str]
    pkgconfig_define: PkgConfigDefineType


class ConfigurationDataSet(TypedDict):

    description: T.Optional[str]

class VcsTag(TypedDict):

    command: T.List[build.CommandTypes]
    fallback: T.Optional[str]
    input: list[CustomTargetInputs]
    output: T.List[str]
    replace_string: str
    install: bool
    install_tag: T.Optional[str]
    install_dir: T.Optional[str]
    install_mode: FileMode


class ConfigureFile(TypedDict):

    output: str
    capture: bool
    format: T.Literal['meson', 'cmake', 'cmake@']
    output_format: T.Literal['c', 'json', 'nasm']
    depfile: T.Optional[str]
    install: T.Optional[bool]
    install_dir: T.Union[str, T.Literal[False]]
    install_mode: FileMode
    install_tag: T.Optional[str]
    encoding: str
    command: T.Optional[T.List[T.Union[build.Executable, Program, Compiler, File, str]]]
    input: T.List[FileOrString]
    configuration: T.Optional[T.Union[T.Dict[str, T.Union[str, int, bool]], build.ConfigurationData]]
    macro_name: T.Optional[str]
    build_subdir: str
    copy: bool


class Subproject(ExtractRequired):

    default_options: T.Dict[OptionKey, options.ElementaryOptionValues]
    version: T.List[str]
    native: MachineChoice


class DoSubproject(ExtractRequired):

    default_options: T.Dict[OptionKey, options.ElementaryOptionValues]
    version: T.List[str]
    cmake_options: T.List[str]
    options: T.Optional[CMakeSubprojectOptions]
    for_machine: MachineChoice


class BaseBuildTarget(TypedDict):

    """Arguments used by all BuildTarget like functions.

    This really exists because Jar is so different than all of the other
    BuildTarget functions.
    """

    build_by_default: bool
    build_rpath: str
    build_subdir: str
    dependencies: T.List[Dependency]
    extra_files: T.List[FileOrString]
    gnu_symbol_visibility: str
    include_directories: T.List[T.Union[str, build.IncludeDirs]]
    install: bool
    install_mode: FileMode
    install_tag: T.Optional[str]
    install_rpath: str
    implicit_include_directories: bool
    link_depends: T.List[T.Union[str, File, build.BuildTargetTypes]]
    link_language: T.Optional[Language]
    link_whole: T.List[build.StaticTargetTypes]
    link_with: T.List[build.LinkableTargetTypes]
    name_prefix: T.Optional[str]
    name_suffix: T.Optional[str]
    native: MachineChoice
    objects: T.List[BuildTargetObjects]
    override_options: T.Dict[str, options.ElementaryOptionValues]
    depend_files: NotRequired[T.List[File]]
    resources: T.List[str]
    vala_header: T.Optional[str]
    vala_vapi: T.Optional[str]
    vala_gir: T.Optional[str]


class BuildTarget(BaseBuildTarget):

    """Arguments shared by non-JAR functions"""

    d_debug: T.List[T.Union[str, int]]
    d_import_dirs: T.List[T.Union[str, build.IncludeDirs]]
    d_module_versions: T.List[T.Union[str, int]]
    d_unittest: bool
    install_dir: T.List[T.Union[str, bool]]
    install_vala_header: T.Union[str, bool, None]
    install_vala_vapi: T.Union[str, bool, None]
    install_vala_gir: T.Union[str, bool, None]
    rust_crate_type: T.Optional[Literal['bin', 'lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro']]
    rust_dependency_map: T.Dict[str, str]
    swift_interoperability_mode: Literal['c', 'cpp']
    swift_module_name: str
    sources: SourcesVarargsType
    link_args: T.List[str]
    link_early_args: T.List[str]
    c_pch: T.Optional[T.Tuple[str, T.Optional[str]]]
    cpp_pch: T.Optional[T.Tuple[str, T.Optional[str]]]
    c_args: T.List[str]
    cpp_args: T.List[str]
    cuda_args: T.List[str]
    fortran_args: T.List[str]
    d_args: T.List[str]
    objc_args: T.List[str]
    objcpp_args: T.List[str]
    rust_args: T.List[str]
    vala_args: T.List[T.Union[str, File]]  # Yes, Vala is really special
    cs_args: T.List[str]
    swift_args: T.List[str]
    cython_args: T.List[str]
    nasm_args: T.List[str]
    masm_args: T.List[str]


class _LibraryMixin(TypedDict):

    rust_abi: T.Optional[RustAbi]


class _LinkableTargetMixin(TypedDict):

    vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]]
    win_subsystem: T.Optional[str]


class _ExecutableMixin(TypedDict):

    export_dynamic: T.Optional[bool]
    gui_app: T.Optional[bool]
    implib: T.Optional[T.Union[str, bool]]
    pie: T.Optional[bool]
    android_exe_type: T.Optional[Literal['application', 'executable']]


class Executable(BuildTarget, _ExecutableMixin, _LinkableTargetMixin):
    pass


class _StaticLibMixin(TypedDict):

    prelink: bool
    pic: T.Optional[bool]


class StaticLibrary(BuildTarget, _StaticLibMixin, _LibraryMixin):
    pass


class _SharedLibMixin(TypedDict):

    darwin_versions: T.Optional[T.Tuple[str, str]]
    soversion: T.Optional[str]
    version: T.Optional[str]
    shortname: str


class SharedLibrary(BuildTarget, _SharedLibMixin, _LibraryMixin, _LinkableTargetMixin):
    pass


class SharedModule(BuildTarget, _LibraryMixin, _LinkableTargetMixin):
    pass


class Library(BuildTarget, _SharedLibMixin, _StaticLibMixin, _LibraryMixin, _LinkableTargetMixin):

    """For library, both_library, and as a base for build_target"""

    c_static_args: NotRequired[T.List[str]]
    c_shared_args: NotRequired[T.List[str]]
    cpp_static_args: NotRequired[T.List[str]]
    cpp_shared_args: NotRequired[T.List[str]]
    cuda_static_args: NotRequired[T.List[str]]
    cuda_shared_args: NotRequired[T.List[str]]
    fortran_static_args: NotRequired[T.List[str]]
    fortran_shared_args: NotRequired[T.List[str]]
    d_static_args: NotRequired[T.List[str]]
    d_shared_args: NotRequired[T.List[str]]
    objc_static_args: NotRequired[T.List[str]]
    objc_shared_args: NotRequired[T.List[str]]
    objcpp_static_args: NotRequired[T.List[str]]
    objcpp_shared_args: NotRequired[T.List[str]]
    rust_static_args: NotRequired[T.List[str]]
    rust_shared_args: NotRequired[T.List[str]]
    vala_static_args: NotRequired[T.List[T.Union[str, File]]]  # Yes, Vala is really special
    vala_shared_args: NotRequired[T.List[T.Union[str, File]]]  # Yes, Vala is really special
    cs_static_args: NotRequired[T.List[str]]
    cs_shared_args: NotRequired[T.List[str]]
    swift_static_args: NotRequired[T.List[str]]
    swift_shared_args: NotRequired[T.List[str]]
    cython_static_args: NotRequired[T.List[str]]
    cython_shared_args: NotRequired[T.List[str]]
    nasm_static_args: NotRequired[T.List[str]]
    nasm_shared_args: NotRequired[T.List[str]]
    masm_static_args: NotRequired[T.List[str]]
    masm_shared_args: NotRequired[T.List[str]]


class _JarMixin(TypedDict):

    main_class: str
    java_resources: T.Optional[build.StructuredSources]
    java_args: T.List[str]


class Jar(BaseBuildTarget, _JarMixin):

    sources: T.Union[str, build.TargetSources, build.ExtractedObjects, build.BuildTarget]


class BuildTargetFunc(Library, _ExecutableMixin, _JarMixin):

    target_type: Literal['executable', 'shared_library', 'static_library',
                         'shared_module', 'both_libraries', 'library', 'jar']


class FuncDeclareDependency(TypedDict):

    compile_args: T.List[str]
    d_import_dirs: T.List[T.Union[build.IncludeDirs, str]]
    d_module_versions: T.List[T.Union[str, int]]
    dependencies: T.List[Dependency]
    extra_files: T.List[FileOrString]
    include_directories: T.List[T.Union[build.IncludeDirs, str]]
    link_args: T.List[str]
    link_whole: T.List[build.StaticTargetTypes]
    link_with: T.List[build.LinkableTargetTypes]
    objects: T.List[build.ExtractedObjects]
    sources: T.List[str | build.TargetSources]
    variables: T.Dict[str, str]
    version: T.Optional[str]


class FuncDependency(ExtractRequired):

    allow_fallback: T.Optional[bool]
    cmake_args: T.List[str]
    cmake_module_path: T.List[str]
    cmake_package_version: str
    components: T.List[str]
    default_options: T.Dict[OptionKey, options.ElementaryOptionValues]
    fallback: T.Union[str, T.List[str], None]
    include_type: IncludeType
    language: T.Optional[Language]
    main: bool
    method: DependencyMethods
    modules: T.List[str]
    native: MachineChoice
    not_found_message: str
    optional_modules: T.List[str]
    private_headers: bool
    static: T.Optional[bool]
    version: T.List[str]


class FuncExpectError(TypedDict):

    how: str


class FuncEnvironment(TypedDict):

    method: Literal['set', 'prepend', 'append']
    separator: str


class MachineMapArgs(TypedDict):

    native: NotRequired[MachineChoice]
    install: NotRequired[bool]
