#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""An interactive rename script.

Opens the list of files from the command line in a text editor, allowing
you to use the editor's features to change the file names.

Usage: %(prog)s <FILES>

Newlines in file names are forbidden.

TODO:
- Handle swaps

"""
import re
import os
import subprocess
import sys
import tempfile


def ask_yn(prompt):
    print(prompt, "(y/n)", end=" ")
    reply = input()
    return reply.strip().lower().startswith("y")


def editandreturn(initial="", suffix=""):
    """Put some text in a temporary file, then open the file in an editor
    and read back the results.

    Parameters
    ----------
    initial : str optional
        The initial text to put in the file.
    suffix : str optional
        A suffix to give the file (e.g. for syntax highlighting).

    Returns
    -------
    str
        The modified text.

    """
    editor = os.environ.get("EDITOR", "vi")

    # Had problems with NamedTemporaryFile in OSX:
    # flushing, seeking, and rereading the file contents did not
    # give me the new edited contents.
    fd, name = tempfile.mkstemp(suffix=suffix)
    try:
        os.write(fd, initial.encode())
        os.fsync(fd)
        subprocess.check_call([editor, name])
        with open(name) as fh:
            return fh.read()
    finally:
        os.unlink(name)
        os.close(fd)


def editandreturn_lines(lines):
    return editandreturn("\n".join(lines)).rstrip("\n").split("\n")


def main(argv):
    confirm = True
    files = argv[1:]
    try:
        if files[0] == "-y":
            confirm = False
            files.pop(0)
    except IndexError:
        pass
    if not files:
        print("Usage: %s [-y] <FILES>" % argv[0])
        return 2

    oldnames = sorted(set(files))
    for name in oldnames:
        if "\n" in name:
            print("File name with newline detected.  Bailing.")
            return 1

    newnames = list(oldnames)
    while True:
        newnames = editandreturn_lines(newnames)
        if not newnames:
            print("No lines.  Exiting.")
            return 0

        newnames_set = set(newnames)
        if len(oldnames) != len(newnames):
            print("Mismatch in number of lines.")
            if confirm and ask_yn("Try again?"):
                continue
            return 1
        elif len(newnames) != len(newnames_set):
            print("Duplicate line.")
            if confirm and ask_yn("Try again?"):
                continue
            return 1
        elif "" in newnames_set:
            print("Blank line")
            if confirm and ask_yn("Try again?"):
                continue
            return 1
        break

    maxoldlen = max(map(len, oldnames))
    maxnewlen = max(map(len, newnames))
    changes = False
    for old, new in zip(oldnames, newnames):
        if old == new:
            print("Skipping %s" % old)
            continue
        elif old in newnames:
            print("Swap detected.  Bailing.")
            return 1
        changes = True
        print("%-*s -> %-*s" % (maxoldlen, old, maxnewlen, new))

    if not changes:
        print("No changes.  Exiting")
        return 0

    if not (confirm and ask_yn("OK?")):
        return 0

    errors = False
    for old, new in zip(oldnames, newnames):
        if old == new:
            continue
        try:
            os.rename(old, new)
        except EnvironmentError as ex:
            print("Rename of %s failed: %s" % (old, ex))
            errors = True
    return 1 if errors else 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))
