#!/usr/bin/env python2.7
#
# mrsgui/scripts/mrsgui-svn-cp/mrsgui-svn-cp ---
#
# See README.rst for info.
# https://bitbucket.org/hbaspecto/mrsgui/src/master/scripts/mrsgui-svn-cp

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

import argparse
#import datetime
#import math
import os
import pdb
import pprint
#import re
import subprocess
import sys
import traceback

#####

RSYNC_EXE="/usr/bin/rsync"

# if we have a chance to use the native svn use it.
# mrsgui-svn-retry might have replaced it.
SVN_EXE = None
for x in ["/usr/bin/svn-exe","/usr/bin/svn"]:
    if os.path.exists(x):
        SVN_EXE=x

SVN_USERNAME = os.environ.get("SVN_USERNAME")
SVN_PASSWORD = os.environ.get("SVN_PASSWORD")
SVN_MESSAGE = os.environ.get("SVN_MESSAGE")

#
#DEBUG= False
DEBUG = True

#
VERBOSE = None
VERSION = ''

#####

def prompt(
        prompt_text,
        choices=None,
        default=None):

    p_tmp = "{}".format(prompt_text)
    if choices:
        # cast all to strings.
        choices = ["{}".format(c) for c in choices]
        p_tmp += "[{}]".format(",".join(choices))
    if default:
        default = "{}".format(default)
        p_tmp += "({})".format(default)

    p_tmp += " >"

    while True:
        line = input(p_tmp)
        line = line.strip()
        print("line={!r}".format(line))
        if line == "":
            return default
        if choices:
            if line not in choices:
                print("    Not in choices.")
                continue
        return line


def prompt_for_paths(s_from_path, s_to_path):

    while True:
        print("mrsgui-svn-cp:")
        print("  1) From path: {!r}".format(s_from_path))
        print("  2) To   path: {!r}".format(s_to_path))
        print("")
        print("  a) Accept and copy")
        print("  q) Cancel and quit")

        val = prompt("Choice",
                     choices=[1, 2, "a", "q"])

        if val is None:
            continue

        elif val == "1":
            s_from_path = prompt(
                "Enter from path:",
                default=s_from_path)

        elif val == "2":
            s_to_path = prompt(
                "Enter to path:",
                default=s_to_path)

        elif val == "a":
            return [s_from_path, s_to_path]

        elif val == "q":
            return None


#####

def rsync_cmd(
        s_from_path,
        s_to_path,
        files_from_lst=None):

    # ensure dest exists.
    # os.makedirs(s_to_path, exist_ok=True)
    try:
        os.makedirs(s_to_path)
    except (os.error,) as e:
        pass

    # if we have a list, use a tmp file.
    files_from_fof = None
    if files_from_lst:
        files_from_fof = "{}.fof".format(s_to_path)
        with open(files_from_fof, "w") as fh:
            fh.write("\n".join(files_from_lst))

    cmd = [
        RSYNC_EXE,
        "-Pa",
    ]
    # option needed?
    if files_from_lst:
        cmd.extend(["--files-from", files_from_fof])
    # Add the rest of the cmd
    cmd.extend([
        s_from_path,
        s_to_path,
    ])

    if DEBUG:
        print("### CMD:", " ".join(cmd))

    # 2.7 to 3.x: call -> run
    # Dont use "run_cmd", we want to show the output.
    # proc = subprocess.run(cmd)
    proc = subprocess.Popen(cmd)
    proc.wait()

    if files_from_fof:
        if not DEBUG:
            os.remove(files_from_fof)

    return True

#####


def run_cmd(cmd, keep_output=True):
    """Run a command and return the proc.
    keep_output does the magic to keep the output.

    """

    if DEBUG:
        print("### CMD:", " ".join(["{}".format(c) for c in cmd]))

    kwargs = {}
    if keep_output:
        kwargs = {
            # which version of python?
            "universal_newlines": True,
            "stdin": None,
            "stdout": subprocess.PIPE,
        }

    # 2.7 -> 3.x
    # proc = subprocess.run(
    proc = subprocess.Popen(
        cmd,
        **kwargs)
    proc.wait()

    return proc


def svn_run_cmd(
        svn_cmd,
        add_user=True,
        add_message=False,
        keep_output=True):
    """Run an svn command with standard options.
    """

    cmd = [
        SVN_EXE,
    ]
    if add_user:
        cmd.extend([
            "--username", SVN_USERNAME,
            "--password", SVN_PASSWORD,
        ])
    if add_message:
        cmd.extend([
            "--message", SVN_MESSAGE,
        ])

    cmd.extend(svn_cmd)

    return run_cmd(
        cmd,
        keep_output=keep_output)

#####


def svn_info(path):
    """Get the output from svn info and parse it.
    Return a dict.

    ::

        Path: /nfs/home/harley/repos/hba/Scenarios/I279
        Working Copy Root Path: /nfs/home/harley/repos/hba/Scenarios/I279
        URL: http://svn.office.hbaspecto.com:8020/svn/pecas/PECASAlberta/Scenarios/I279
        Relative URL: ^/PECASAlberta/Scenarios/I279
        ...
    """

    if not os.path.isdir(path):
        print("!!! svn_info: not a dir: {!r}".format(path))
        return None

    proc = svn_run_cmd([
        "info",
        path,
    ])

    if proc.returncode != 0:
        print("svn info: returncode={!r}".format(proc.returncode))
        return None

    data = {}
    for line in proc.stdout.read().split("\n"):
        line = line.strip()
        if line == "":
            continue
        (key, val) = line.split(": ", 1)
        data[key] = val

    pprint.pprint(data)
    return data


def svn_ls_files(path, depth=None):
    """Return a list of all files in the repo at path.
    (Dont include directories)
    """

    depth = depth or "infinity"

    proc = svn_run_cmd([
        "list",
        "--depth", depth,
        path])

    files_lst = []
    for line in proc.stdout.read().split("\n"):
        if line.endswith("/"):
            continue
        if line == "":
            continue
        #
        files_lst.append(line)

    file_lst = sorted(files_lst)
    return files_lst


def svn_url_exists(url):
    proc = svn_run_cmd([
        "info",
        url])

    #
    if proc.returncode == 0:
        return True

    return False


def svn_copy(from_url, to_url):
    proc = svn_run_cmd(
        [
            "copy",
            from_url,
            to_url,
        ],
        add_message=True)

    if proc.returncode != 0:
        raise ValueError("copy failed.")

    return True


def svn_switch(url, path):
    proc = svn_run_cmd(
        [
            "switch",
            url,
            path,
        ])

    if proc.returncode != 0:
        raise ValueError("switch failed.")

    return True


def copy_and_svn_switch(
        s_from_path,
        s_to_path):
    """Do all the steps to copy and swich:

    - check the data is ok
    - copy data on the local filesystem
    - figure out the new url
    - copy data in svn with the new url.
    - do the svn switch.
    - Done!
    """

    print("### copy_and_svn_switch:")
    print("    From: {!r}".format(s_from_path))
    print("    To:   {!r}".format(s_to_path))

    # Check conditions...
    if os.path.exists(s_to_path):
        print("!!! copy_and_svn_switch: to path exists!")
        return -1

    s_from_info = svn_info(s_from_path)
    if s_from_info is None:
        return -1

    # Figure out the urls.
    s_from_url = s_from_info.get("URL")
    if not s_from_url:
        raise ValueError("shouldnt happen.")
    (s_from_base, s_from_url_name) = s_from_url.rsplit("/", 1)
    if s_from_url_name != os.path.basename(s_from_path):
        raise ValueError("Dir names dont match!")

    #
    s_to_name = os.path.basename(s_to_path)
    s_to_url = "{}/{}".format(s_from_base, s_to_name)

    # exists on svn?
    rv = svn_url_exists(s_to_url)
    if rv:
        print("!!! svn path exists: {!r}".format(s_to_url))
        return 1

    #
    s_from_files_lst = svn_ls_files(s_from_path)
    pprint.pprint(s_from_files_lst[0:20])

    # These two could take a long time and is restartable.
    # So do them first...
    # ...the files...
    rsync_cmd(
        s_from_path,
        s_to_path,
        files_from_lst=s_from_files_lst)
    # ...and the svn directory.
    rsync_cmd(
        s_from_path + "/.svn/",
        s_to_path + "/.svn/")

    # Copy finished, do the updates.
    svn_copy(
        s_from_url,
        s_to_url)
    svn_switch(
        s_to_url,
        s_to_path)

    return 0

#####


def main(raw_args):
    global DEBUG, VERBOSE, VERSION
    global SVN_USERNAME, SVN_PASSWORD, SVN_MESSAGE, SVN_EXE
    global RSYNC_EXE

    #
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter)
    parser.description = """
DESCRIPTION:

"""
    parser.epilog = """
EXAMPLES:

"""
    #
    g = parser.add_argument_group('GENERAL')
    g.add_argument("--debug", "-d",
                   action="store_true",
                   help="Turn on the debugging flag.")
    g.add_argument("--pdb",
                   action="store_true",
                   help=argparse.SUPPRESS)
    g.add_argument("--verbose", "-v",
                   action="store_true",
                   help="Be more verbose.")

    g = parser.add_argument_group('SVN ARGS')

    g.add_argument("--username", "-u",
                   help="svn username (required)")

    g.add_argument("--password", "-p",
                   help="svn password (required)")

    g.add_argument("--message", "-m",
                   help="svn message (required)")

    # Hidden options for programmer
    g.add_argument("--rsync-exe",
                   help=argparse.SUPPRESS)

    g.add_argument("--svn-exe",
                   help=argparse.SUPPRESS)

    g = parser.add_argument_group('MRSGUI ARGS')

    g.add_argument("--do-prompt",
                   action="store_true",
                   default=True,
                   help="Prompt for input.")
    g.add_argument("--no-prompt",
                   action="store_const",
                   const=False,
                   dest="do_prompt",
                   help="Dont prompt for input.")

    #
    g = parser.add_argument_group('ARGS')
    g.add_argument("args", nargs="*",
                   help="The remaining args.")

    #
    args = parser.parse_args(raw_args)

    #
    if args.pdb:
        pdb.set_trace()
    if args.debug:
        DEBUG = args.debug
    if args.verbose:
        VERBOSE = args.verbose

    #
    if args.rsync_exe:
        RSYNC_EXE=args.rsync_exe
    #
    if args.svn_exe:
        SVN_EXE=args.svn_exe
    if SVN_EXE is None:
        print("!!! didnt find SVN_EXE")
    #
    if args.username:
        SVN_USERNAME = args.username
    elif SVN_USERNAME:
        pass
    else:
        print("!!! The username arg is required.")
        return 1
    #
    if args.password:
        SVN_PASSWORD = args.password
    elif SVN_PASSWORD:
        pass
    else:
        print("!!! The password arg is required.")
        return 1
    #
    if args.message:
        SVN_MESSAGE = args.message
    elif SVN_MESSAGE:
        pass
    else:
        print("!!! The message arg is required.")
        return 1

    #
    s_from_path = None
    s_to_path = None

    if 0 < len(args.args):
        s_from_path = args.args[0]
    if 1 < len(args.args):
        s_to_path = args.args[1]

    try:
        if args.do_prompt:
            rv = prompt_for_paths(s_from_path, s_to_path)
            if rv is None:
                return 1
            (s_from_path, s_to_path) = rv

        copy_and_svn_switch(s_from_path, s_to_path)
    except (KeyboardInterrupt,) as e:
        print("!!! Exit on Ctrl-C")
        return 1

    #
    return 0

#####


def main_entry():
    sys.exit(main(sys.argv[1:]))


if __name__ == "__main__":
    main_entry()

# Local Variables:
# mode: python
# End:
