#!/usr/bin/env python3


import argparse
import contextlib
import os
import shutil
import subprocess
import sys


def format_title(title):
    box = {
        "tl": "╔",
        "tr": "╗",
        "bl": "╚",
        "br": "╝",
        "h": "═",
        "v": "║",
    }
    hline = box["h"] * (len(title) + 2)

    return "\n".join(
        [
            f"{box['tl']}{hline}{box['tr']}",
            f"{box['v']} {title} {box['v']}",
            f"{box['bl']}{hline}{box['br']}",
        ]
    )


def rm_rf(path):
    try:
        shutil.rmtree(path)
    except FileNotFoundError:
        pass


def sanitize_path(name):
    return name.replace("/", "-")


def get_current_revision():
    revision = subprocess.check_output(
        ["git", "rev-parse", "--abbrev-ref", "HEAD"], encoding="utf-8"
    ).strip()

    if revision == "HEAD":
        # This is a detached HEAD, get the commit hash
        revision = (
            subprocess.check_output(["git", "rev-parse", "HEAD"])
            .strip()
            .decode("utf-8")
        )

    return revision


@contextlib.contextmanager
def checkout_git_revision(revision):
    current_revision = get_current_revision()
    subprocess.check_call(["git", "checkout", "-q", revision])

    try:
        yield
    finally:
        subprocess.check_call(["git", "checkout", "-q", current_revision])


def build_install(revision):
    build_dir = "_build"
    dest_dir = os.path.abspath(sanitize_path(revision))
    print(
        format_title(f"# Building and installing {revision} in {dest_dir}"),
        end="\n\n",
        flush=True,
    )

    with checkout_git_revision(revision):
        rm_rf(build_dir)
        rm_rf(revision)

        subprocess.check_call(
            [
                "meson",
                build_dir,
                "--prefix=/usr",
                "--libdir=lib",
                "-Dauto_features=disabled",
                "-Db_coverage=false",
                "-Dtests=false",
            ]
        )
        subprocess.check_call(["ninja", "-v", "-C", build_dir])
        subprocess.check_call(
            ["ninja", "-v", "-C", build_dir, "install"], env={"DESTDIR": dest_dir}
        )

    return dest_dir


def compare(old_tree, new_tree):
    print(format_title(f"# Comparing the two ABIs"), end="\n\n", flush=True)

    old_headers = os.path.join(old_tree, "usr", "include")
    old_lib = os.path.join(old_tree, "usr", "lib", "libfwupd.so")

    new_headers = os.path.join(new_tree, "usr", "include")
    new_lib = os.path.join(new_tree, "usr", "lib", "libfwupd.so")

    subprocess.check_call(
        [
            "abidiff",
            "--headers-dir1",
            old_headers,
            "--headers-dir2",
            new_headers,
            "--drop-private-types",
            "--suppressions",
            "contrib/ci/abidiff.suppr",
            "--fail-no-debug-info",
            "--no-added-syms",
            old_lib,
            new_lib,
        ]
    )


if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument("old", help="the previous revision, considered the reference")
    parser.add_argument("new", help="the new revision, to compare to the reference")

    args = parser.parse_args()

    if args.old == args.new:
        print("Let's not waste time comparing something to itself")
        sys.exit(0)

    old_tree = build_install(args.old)
    new_tree = build_install(args.new)

    try:
        compare(old_tree, new_tree)

    except subprocess.CalledProcessError:
        sys.exit(1)

    print(f"Hurray! {args.old} and {args.new} are ABI-compatible!")
