# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2018 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>

import sys
import fnmatch
import re

# Same folder
import config


def error(line_i, message):
    """ Print a configure.ac error message with the line number.
        :param line_i: the zero based line counter """
    print("ERROR: configure.ac line " + str(line_i+1) + ": " + message)
    sys.exit(1)


def repository(library, version):
    """ Find the git repository that contains a certain library. Based on the
        information in config.py.

        :param library: the name as referenced in the PKG_CHECK_MODULES
                        statement. For example: "libosmoabis"
        :param version: for example "0.5.0"
        :returns: the repository name, e.g. "libosmo-abis" """
    for repo, libraries in config.repos.items():
        if library in libraries:
            print(" * " + library + ":" + version + " (part of " + repo + ")")
            return repo

    print(" * " + library + ":" + version)
    return library


def library_is_relevant(library):
    """ :returns: True when we would build the library in question from source,
                  False otherwise. """
    for pattern in config.relevant_library_patterns:
        if fnmatch.fnmatch(library, pattern):
            return True
    return False


def parse_condition(line):
    """ Find the PKG_CHECK_MODULES conditions in any line from a configure.ac.

        Example lines:
        PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore  >= 0.10.0)
        PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd)

        :returns: * None when there's no condition in that line
                  * a string like "libosmocore  >= 0.1.0" """
    # Only look at PKG_CHECK_MODULES lines
    if "PKG_CHECK_MODULES" not in line:
        return
    if re.search('#.*PKG_CHECK_MODULES', line):
        return

    # Extract the condition
    ret = line.split(",")[1].split(")")[0].strip()

    # Only look at Osmocom libraries
    library = ret.split(" ")[0]
    if library_is_relevant(library):
        return ret


def library_version(line_i, condition):
    """ Get the library and version strings from a condition.
        :param line_i: the zero based line counter
        :param condition: a condition like "libosmocore  >= 0.1.0" """
    # Split by space and remove empty list elements
    split = list(filter(None, condition.split(" ")))
    if len(split) != 3:
        error(line_i, "invalid condition format, expected something"
                      " like 'libosmocore >= 0.10.0' but got: '" +
                      condition + "'")
    library, operator, version = split

    # Right operator
    if operator == ">=":
        return (library, version)

    # Wrong operator
    error(line_i, "invalid operator, expected '>=' but got: '" +
                  operator + "'")


def configure_ac(workdir, repo):
    """ Parse the PKG_CHECK_MODULES statements of a configure.ac file.

        :param workdir: path to where all data (git, build, install) is stored
        :param repo: the repository to look at (e.g. "osmo-bts")
        :returns: a dictionary like the following:
                  {"libosmocore": "0.11.0",
                   "libosmo-abis": "0.5.0"} """
    # Read configure.ac
    path = workdir + "/git/" + repo + "/configure.ac"
    with open(path) as handle:
        lines = handle.readlines()

    # Parse the file into ret
    ret = {}
    for i in range(0, len(lines)):
        # Parse the line
        condition = parse_condition(lines[i])
        if not condition:
            continue
        (library, version) = library_version(i, condition)

        # Add to ret (with duplicate check)
        repo_dependency = repository(library, version)
        if repo_dependency in ret and version != ret[repo_dependency]:
            error(i, "found multiple PKG_CHECK_MODULES statements for " +
                     repo_dependency + ".git, and they have different"
                     " versions!")
        ret[repo_dependency] = version
    return ret
