# A representation of shared libraries and their module
# representation in a Python package.
#
# Author: Malte J. Ziebarth (malte.ziebarth@tum.de)
#
# Copyright (C) 2025-2026 Technische Universität München
#
# Licensed under the EUPL, Version 1.2 or – as soon they will be approved by
# the European Commission - subsequent versions of the EUPL (the "Licence");
# You may not use this work except in compliance with the Licence.
# You may obtain a copy of the Licence at:
#
# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Licence is distributed on an "AS IS" basis,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the Licence for the specific language governing permissions and
# limitations under the Licence.
#
# SPDX-FileCopyrightText: 2025-2026 Technical University of Munich
# SPDX-License-Identifier: EUPL-1.2
# SPDX-FileContributor: Malte J. Ziebarth <malte.ziebarth@tum.de>

import re
from sys import platform
from ..language.abstract import Struct
from ..language.python import CTypeType
from ..language.arrays import NDArray, QuantityArray
from ..repo import Repository, IncludePath, RelativePath
from .function import Function

_MAIL_CHECK = re.compile('\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b')



class Library:
    """
    A representation of a library API.
    """
    module: tuple[str,...]
    package: str
    authors: list[tuple[str, str | None]]
    copyright: list[str]
    license_boilerplate: str | None

    functions: list[Function]
    structs: list[Struct[CTypeType]]

    include_root: RelativePath
    original_header: RelativePath
    wrapper_header_file: RelativePath

    def __init__(self,
            functions: list[Function],
            structs: list[Struct[CTypeType]],
            module: tuple[str, ...],
            repository: Repository,
            authors: list[tuple[str, str | None]],
            copyright: list[str],
            license_boilerplate: str | None,
            include_root: RelativePath,
            original_header: RelativePath,
            wrapper_header: RelativePath,
            accept_any_email: bool = False
        ):
        # Sanity:
        assert isinstance(module, tuple)
        assert all(isinstance(m, str) for m in module)
        # Check the package conforms to Python naming (mostly)
        package_alpha = repository.package_name.replace('.','').replace('_','')
        assert package_alpha.isalnum()
        assert package_alpha[0].isalpha()
        # Check the name format:
        assert isinstance(authors, list)
        for a in authors:
            assert isinstance(a, tuple)
            assert len(a) == 2
            nm, mail = a
            assert isinstance(nm, str) and isinstance(mail, str)
            if not accept_any_email:
                print("mail: '" + str(mail) + "'.")
                assert _MAIL_CHECK.match(mail.upper())
        # Copyright:
        assert isinstance(copyright,list)
        assert all(isinstance(c,str) for c in copyright)
        assert isinstance(license_boilerplate, str) or license_boilerplate is None
        # Functions and structs:
        assert isinstance(functions, list)
        assert all(isinstance(f, Function) for f in functions)
        assert isinstance(structs, list)
        assert all(isinstance(s, Struct) for s in structs)
        assert isinstance(include_root, RelativePath)
        assert isinstance(original_header, RelativePath)
        assert isinstance(wrapper_header, RelativePath)

        self.module = module
        self.package = repository.package_name
        self.authors = authors
        self.copyright = copyright
        self.license_boilerplate = license_boilerplate
        self.functions = functions
        self.structs = structs
        self.original_header = original_header
        self.wrapper_header_file = wrapper_header
        self.include_root = include_root


    def shared_library_name(self) -> str:
        """
        Name of the generated shared library object.
        """
        if platform in ("linux", "freebsd"):
            suffix = "so"
        elif platform in ("win32", "cygwin"):
            suffix = "dll"
        else:
            raise NotImplementedError(
                f"Platform '{platform}' not supported."
            )
        return f"lib{self.module[-1]}.{suffix}"


    def requires_numpy(self) -> bool:
        """
        Check whether this library needs a NumPy import.
        """
        for fun in self.functions:
            for arg,_ in fun.cparams:
                if isinstance(arg.val, (NDArray, QuantityArray)):
                    return True

        return False


    def header_include(self) -> IncludePath:
        """
        Return the include path for the header.
        """
        return IncludePath(
            self.original_header,
            self.include_root
        )


    def wrapper_include(self) -> IncludePath:
        """
        Return the include path for the wrapper header.
        """
        return IncludePath(
            self.wrapper_header_file,
            self.include_root
        )