#!/usr/bin/env python3
# Copyright (C) 2017 The Antares Authors
# This file is part of Antares, a tactical space combat game.
# Antares is free software, distributed under the LGPL+. See COPYING.

from __future__ import division, print_function, unicode_literals

import argparse
import collections
import os
import platform
import sys

try:
    from shlex import quote
except ImportError:
    from pipes import quote

sys.path.append(os.path.join(os.path.dirname(__file__), "scripts"))
import installdeps

sys.path.append(os.path.join(os.path.dirname(__file__), "build", "lib", "scripts"))
try:
    import cfg
except ImportError:
    pass

str = type("")  # For Python2


def main():
    os.chdir(os.path.dirname(sys.argv[0]))
    parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument("-m",
                        "--mode",
                        metavar="MODE",
                        type=str,
                        choices="dbg dev opt".split(),
                        default="opt",
                        help="set build configuration:\n"
                        "  - opt: compile for fast binaries (default)\n"
                        "  - dev: compile for fast builds\n"
                        "  - dbg: add debugging symbols")
    parser.add_argument("-o",
                        "--target-os",
                        metavar="OS",
                        type=str,
                        help="target os (default: host os)")
    parser.add_argument("--prefix",
                        type=str,
                        default="/usr/local",
                        help="installation prefix (default: /usr/local)")
    parser.add_argument("-v",
                        "--antares-version",
                        metavar="X.X.X",
                        type=str,
                        help="override antares_version string")
    args = parser.parse_args()

    check_submodules()
    check_host()
    check_target(args)

    with cfg.step("configure mode") as msg:
        msg(args.mode, color="green")
    gn_args = {}
    gn_args["mode"] = args.mode
    gn_args["target_os"] = args.target_os
    gn_args["prefix"] = args.prefix
    if args.antares_version:
        gn_args["antares_version"] = args.antares_version
    if args.target_os == "mac":
        gn_args["macosx_version_min"] = "10.7"
    cfg.gn(**gn_args)

    print("make(1) it so!")


def check_submodules():
    REQUIRED_SUBMODULES = [
        "build/lib/BUILD.gn",
        "ext/glfw/BUILD.gn",
        "ext/gmock/BUILD.gn",
        "ext/libmodplug/BUILD.gn",
        "ext/libpng/BUILD.gn",
        "ext/libsfz/BUILD.gn",
        "ext/libsndfile/BUILD.gn",
        "ext/libzipxx/BUILD.gn",
        "ext/procyon/BUILD.gn",
        "data/README.md",
    ]
    missing = False
    for module in REQUIRED_SUBMODULES:
        if not os.path.exists(os.path.join(module)):
            missing = True
            break

    if missing:
        print("Some submodules are missing. Run:")
        print("")
        print("  $ git submodule update --init build ext data")
        print("")
        print("Then, try ./configure again")
        sys.exit(1)


def check_host():
    with cfg.step("checking host os") as msg:
        if cfg.host_os() in ["mac", "linux"]:
            msg(cfg.host_os(), color="green")
        else:
            msg(cfg.host_os(), color="red")
            print("\nSorry! Antares requires Mac OS X or Linux")
            sys.exit(1)


def check_target(args):
    with cfg.step("checking target os") as msg:
        if args.target_os is None:
            args.target_os = cfg.host_os()
        checker = {
            ("mac", "mac"): check_mac,
            ("linux", "linux"): check_linux_native,
            ("linux", "win"): check_win_on_linux,
        }.get((cfg.host_os(), args.target_os))
        if checker is None:
            msg(args.target_os, color="red")
            sys.exit(1)
        msg(args.target_os, color="green")
    checker(args)


def check_mac(args):
    with cfg.step("checking Mac OS X version") as msg:
        ver = platform.mac_ver()[0]
        ver = tuple(int(x) for x in ver.split(".")[:2])
        if ver < (10, 9):
            msg("%d.%d" % ver, color="red")
            print("\nSorry! Antares requires Mac OS X 10.9+")
            sys.exit(1)
        msg("%d.%d" % ver, color="green")

    missing = collections.OrderedDict()
    if not (check_clang() and check_libcxx()):
        missing["xcode"] = ("* To install Xcode, open the App Store:\n"
                            "    https://itunes.apple.com/en/app/xcode/id497799835\n"
                            "  After installing, open it and accept the license agreement\n")

    if missing:
        print("\nmissing dependencies: %s\n" % " ".join(missing.keys()))
        for step in missing.values():
            sys.stdout.write(step)
        print("")
        print("Then, try ./configure again")
        sys.exit(1)


def check_linux_native(args):
    with cfg.step("checking Linux distro") as msg:
        distro = installdeps.linux_distribution()
        if distro[0].lower() in installdeps.COMMAND:
            msg(" ".join(distro), color="green")
            distro = distro[0]
        else:
            msg(" ".join(distro) + " (untested)", color="yellow")
            distro = "Ubuntu"
        command = installdeps.COMMAND[distro.lower()]
        package = installdeps.PACKAGE[distro.lower()]

    missing_pkgs = []
    if not check_clang("clang++"):
        missing_pkgs.append("clang")
    if not check_libcxx("clang++"):
        missing_pkgs.append("libc++")
    if not check_libcxxabi("clang++"):
        missing_pkgs.append("libc++abi")

    if check_pkg_config():
        libs = """
            gl glfw3 glu libmodplug libzip libpng neon openal sndfile
            x11 xcursor xinerama xrandr xxf86vm zlib
        """.split()
        for lib in libs:
            if not cfg.check_pkg(lib):
                missing_pkgs.append(lib)
    else:
        missing_pkgs.append("pkg-config")

    if missing_pkgs:
        print("\nmissing dependencies: %s" % " ".join(missing_pkgs))
        if len(missing_pkgs) == 1:
            print("On %s, you can install it with:\n" % distro)
        else:
            print("On %s, you can install them with:\n" % distro)
        command = ["sudo"] + command + list(package[pkg] for pkg in missing_pkgs)
        print("    $ %s\n" % (" ".join(quote(arg) for arg in command)))
        print("Then, try ./configure again")
        sys.exit(1)


def check_win_on_linux(args):
    with cfg.step("checking Linux distro") as msg:
        distro = installdeps.linux_distribution()
        if distro == ("Ubuntu", "20.04", "focal"):
            msg(" ".join(distro), color="green")
        else:
            msg(" ".join(distro), color="red")
            print("\nSorry! Cross-compilation currently requires Ubuntu 20.04 focal")
            sys.exit(1)

    missing = collections.OrderedDict()
    if not check_clang("clang++"):
        missing["clang"] = "clang"

    with cfg.step("checking for mingw") as msg:
        if os.path.exists("/usr/x86_64-w64-mingw32/include/windows.h"):
            msg("ok", color="green")
        else:
            msg("missing", color="red")
            missing["mingw"] = "mingw-w64"

    if missing:
        print("\nmissing dependencies: %s" % " ".join(missing.keys()))
        if len(missing) == 1:
            print("\nYou can install it with:\n")
        else:
            print("\nYou can install them with:\n")
        print("    $ sudo apt-get install %s" % (" ".join(missing.values())))
        sys.exit(1)


def check_clang(executable=""):
    """Compile a basic C++11 binary."""
    executable = executable or "clang++"
    return cfg.check_bin(("%s -x c++ -std=c++11 - -o /dev/null" % executable).split(),
                         what="clang",
                         input="int main() { return 1; }")


def check_libcxx(executable=""):
    """Compile a basic C++11, libc++ binary."""
    executable = executable or "clang++"
    return cfg.check_bin(
        ("%s -x c++ -std=c++11 -stdlib=libc++ - -o /dev/null" % executable).split(),
        what="libc++",
        input="#include <chrono>\n\nint main() { return std::chrono::seconds(1).count(); }")


def check_libcxxabi(executable=""):
    """Compile a basic C++11, libc++ binary, including cxxabi.h.

    Pass -I/usr/include/libcxxabi explicitly to work around a broken
    environment on Ubuntu.
    """
    executable = executable or "clang++"
    return cfg.check_bin(
        ("%s -x c++ -std=c++11 -stdlib=libc++ -I/usr/include/libcxxabi - -o /dev/null" %
         executable).split(),
        what="libc++abi",
        input="#include <cxxabi.h>\n\nint main() { return 0; }")


def check_pkg_config():
    """Run pkg-config --version."""
    return cfg.check_bin("pkg-config --version".split())


if __name__ == "__main__":
    main()
