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

import collections
import os
import subprocess
import sys

# Same folder
import parse


def git_clone(workdir, prefix, cache_git_fetch, repository, version):
    """ Clone a missing git repository and checkout a specific version tag.

        :param workdir: path to where all data (git, build, install) is stored
        :param prefix: git url prefix (e.g. "https://gerrit.osmocom.org/")
        :param cache_git_fetch: list of repositories that have already been
                                fetched in this run of osmo-depcheck
        :param repository: Osmocom git repository name (e.g. "libosmo-abis")
        :param version: "master" or a version tag like "0.11.0" """
    repodir = workdir + "/git/" + repository
    if repository not in cache_git_fetch:
        if os.path.exists(repodir):
            # Fetch tags for existing source
            print("Fetching tags...")
            subprocess.run(["git", "-C", repodir, "fetch", "--tags", "-q"],
                           check=True)
        else:
            # Clone the source
            url = prefix + repository
            print("Cloning git repo: " + url)
            try:
                subprocess.run(["git", "-C", workdir + "/git", "clone", "-q",
                                url], check=True)
            except subprocess.CalledProcessError:
                print("NOTE: if '" + repository + "' is part of a git"
                      " repository with a different name, please add it to the"
                      " mapping in 'config.py' and try again.")
                sys.exit(1)

        # Only fetch the same repository once per session
        cache_git_fetch.append(repository)

    # Checkout the version tag
    try:
        subprocess.run(["git", "-C", repodir, "checkout", version, "-q"],
                       check=True)
    except subprocess.CalledProcessError:
        print("ERROR: git checkout failed! Invalid version specified?")
        sys.exit(1)


def generate(workdir, prefix, cache_git_fetch, initial, rev):
    """ Generate the dependency graph of an Osmocom program by cloning the git
        repository, parsing the "configure.ac" file, and recursing.

        :param workdir: path to where all data (git, build, install) is stored
        :param prefix: git url prefix (e.g. "https://gerrit.osmocom.org/")
        :param cache_git_fetch: list of repositories that have already been
                                fetched in this run of osmo-depcheck
        :param initial: the first program to look at (e.g. "osmo-bts")
        :param rev: the git revision to check out ("master", "0.1.0", ...)
        :returns: a dictionary like the following:
                  {"osmo-bts": {"version": "master",
                                "depends": {"libosmocore": "0.11.0",
                                            "libosmo-abis": "0.5.0"}},
                   "libosmocore": {"version": "0.11.0",
                                   "depends": {}},
                   "libosmo-abis": {"version": "0.5.0",
                                    "depends": {"libosmocore": "0.11.0"}} """
    # Iterate over stack
    stack = collections.OrderedDict({initial: rev})
    ret = collections.OrderedDict()
    while len(stack):
        # Pop program from stack
        program, version = next(iter(stack.items()))
        del stack[program]

        # Skip when already parsed
        if program in ret:
            continue

        # Add the programs dependencies to the stack
        print("Looking at " + program + ":" + version)
        git_clone(workdir, prefix, cache_git_fetch, program, version)
        depends = parse.configure_ac(workdir, program)
        stack.update(depends)

        # Add the program to the ret
        ret[program] = {"version": version, "depends": depends}

    return ret


def print_dict(depends):
    """ Print the whole dependency graph.
        :param depends: return value from generate() above """
    print("Dependency graph:")

    for program, data in depends.items():
        version = data["version"]
        depends = data["depends"]
        print(" * " + program + ":" + version + " depends: " + str(depends))


def git_latest_tag(workdir, repository):
    """ Get the last release string by asking git for the latest tag.

        :param workdir: path to where all data (git, build, install) is stored
        :param repository: Osmocom git repository name (e.g. "libosmo-abis")
        :returns: the latest git tag (e.g. "1.0.2") """
    dir = workdir + "/git/" + repository
    complete = subprocess.run(["git", "-C", dir, "describe", "--abbrev=0",
                               "master"], check=True, stdout=subprocess.PIPE)
    return complete.stdout.decode().rstrip()


def print_old(workdir, depends):
    """ Print dependencies tied to an old release tag

        :param workdir: path to where all data (git, build, install) is stored
        :param depends: return value from generate() above """
    print("Dependencies on old releases:")

    for program, data in depends.items():
        for depend, version in data["depends"].items():
            latest = git_latest_tag(workdir, depend)
            if latest == version:
                continue
            print(" * " + program + ":" + data["version"] + " -> " +
                  depend + ":" + version + " (latest: " + latest + ")")
