#!/usr/bin/env python3
import argparse
import os
import subprocess
import xml.etree.ElementTree as ET
import sys

from common import git
from common import Colors
from common import accept_command


SCRIPTDIR = os.path.normpath(os.path.dirname(__file__))


def manifest_get_commits(manifest):
    res = {}
    tree = ET.parse(manifest)
    root = tree.getroot()
    for child in root:
        if child.tag == 'project':
            res[child.attrib["name"]] = child.attrib["revision"]
    return res


def update_subprojects(manifest, no_interaction=False):
    if manifest:
        repos_commits = manifest_get_commits(manifest)
    else:
        repos_commits = {}

    subprojects_dir = os.path.join(SCRIPTDIR, "subprojects")
    for repo_name in os.listdir(subprojects_dir):
        repo_dir = os.path.normpath(os.path.join(SCRIPTDIR, subprojects_dir, repo_name))
        if not os.path.exists(os.path.join(repo_dir, '.git')):
            continue
        revision = repos_commits.get(repo_name)
        if not update_repo(repo_name, repo_dir, revision, no_interaction):
            return False

    return True


def update_repo(repo_name, repo_dir, revision, no_interaction, recurse_i=0):
    print("Updating %s..." % repo_name)
    git("config", "rebase.autoStash", "true", repository_path=repo_dir)
    try:
        if revision:
            git("fetch", repository_path=repo_dir)
            git("checkout", revision, repository_path=repo_dir)
        else:
            git("pull", "--rebase", repository_path=repo_dir)
        git("submodule", "update", repository_path=repo_dir)
    except Exception as e:
        out = getattr(e, "output", b"").decode()
        if not no_interaction:
            print("====================================="
                  "\n%s\nEntering a shell in %s to fix that"
                  " just `exit 0` once done, or `exit 255`"
                  " to skip update for that repository"
                  "\n=====================================" % (
                        out, repo_dir))
            try:
                if os.name is 'nt':
                    shell = os.environ.get("COMSPEC", r"C:\WINDOWS\system32\cmd.exe")
                else:
                    shell = os.environ.get("SHELL", os.path.realpath("/bin/sh"))
                subprocess.check_call(shell, cwd=repo_dir)
            except subprocess.CalledProcessError as e:
                if e.returncode == 255:
                    print("Skipping '%s' update" % repo_name)
                    return True
            except:
                # Result of subshell does not really matter
                pass

            if recurse_i < 3:
                return update_repo(repo_name, repo_dir, revision, no_interaction,
                                    recurse_i + 1)
            return False
        else:
            print("\nCould not rebase %s, please fix and try again."
                    " Error:\n\n%s %s" % (repo_dir, out, e))

            return False


    commit_message = git("show", repository_path=repo_dir).split("\n")
    print(u"  -> %s%s%s - %s" % (Colors.HEADER, commit_message[0][7:14], Colors.ENDC,
                                    commit_message[4].strip()))

    return True


if __name__ == "__main__":
    parser = argparse.ArgumentParser(prog="git-update")

    parser.add_argument("--no-color",
                        default=False,
                        action='store_true',
                        help="Do not output ansi colors.")
    parser.add_argument("--builddir",
                        default=None,
                        help="Specifies the build directory where to"
                        " invoke ninja after updating.")
    parser.add_argument("--no-interaction",
                        default=False,
                        action='store_true',
                        help="Do not allow interaction with the user.")
    parser.add_argument("--manifest",
                        default=None,
                        help="Use a android repo manifest to sync repositories"
                        " Note that it will let all repositories in detached state")
    options = parser.parse_args()
    if options.no_color:
        Colors.disable()

    if options.no_interaction:
        sys.stdin.close()

    if not update_repo('gst-build', SCRIPTDIR, None, options.no_interaction):
        exit(1)
    if not update_subprojects(options.manifest, options.no_interaction):
        exit(1)

    if options.builddir:
        ninja = accept_command(["ninja", "ninja-build"])
        if not ninja:
            print("Can't find ninja, other backends are not supported for rebuilding")
            exit(1)

        if not os.path.exists(os.path.join (options.builddir, 'build.ninja')):
            print("Can't rebuild in %s as no build.ninja file found." % options.builddir)

        print("Rebuilding all GStreamer modules.")
        exit(subprocess.call([ninja, '-C', options.builddir]))