#!/usr/bin/env python

import sys
import subprocess as subp
import urwid


def silent_call(cmd_list):
    return subp.call(cmd_list, stdout=subp.PIPE, stderr=subp.PIPE)


def current_directory_git_repo():
    cmd = 'git status'.split()
    return silent_call(cmd) == 0


def git_branch(branch):
    cmd = 'git show-ref --verify --quiet refs/heads/{0}'.format(branch).split()
    return silent_call(cmd) == 0


def get_commits(branch):
    cmd = 'git log {0} ^HEAD --no-merges --oneline'.format(branch).split()
    proc = subp.Popen(cmd, stdout=subp.PIPE, stderr=subp.PIPE)
    out = proc.communicate()[0]
    lines = out.rstrip().split('\n')

    def split_head(s):
        split = s.split()
        return (split[0], ' '.join(split[1:]))

    return [split_head(s) for s in lines if s != '']


def cherry_pick(commits):
    for commit in reversed(commits):
        cmd = 'git cherry-pick {0}'.format(commit).split()
        if silent_call(cmd) == 0:
            print("Picked {0} successfully".format(commit))
        else:
            sys.exit("Could not pick {0}. Fix change and commit with `git "
                     "commit -c {0}' to keep authorship.".format(commit))


class Application(object):
    def __init__(self, commits):
        self.picked = []
        self.commits = commits

    def run(self):
        self.commit_box = self.get_commit_list_box()
        loop = urwid.MainLoop(self.commit_box,
                              unhandled_input=self.handle_input)
        loop.run()

    def get_commit_list_box(self):
        body = [urwid.Text(u"Choose commits to cherry-pick and accept with `q'"),
                urwid.Divider()]

        for commit, message in self.commits:
            box = urwid.CheckBox(u'{0}: {1}'.format(commit, message))
            urwid.connect_signal(box, 'change', self.commit_chosen, commit)
            body.append(urwid.AttrMap(box, None, focus_map='reversed'))

        return urwid.ListBox(urwid.SimpleListWalker(body))

    def commit_chosen(self, button, checked, commit):
        if checked:
            self.picked.append(commit)
            widget, position = self.commit_box.get_focus()
            self.commit_box.set_focus(position + 1)
        else:
            self.picked.remove(commit)

    def handle_input(self, key):
        if key in ('q', 'Q'):
            raise urwid.ExitMainLoop()


if __name__ == '__main__':
    if not current_directory_git_repo():
        sys.exit("Current directory is not a Git repository")

    if len(sys.argv) < 2:
        sys.exit("No branch to cherry pick from is given")

    branch = sys.argv[1]

    if not git_branch(branch):
        sys.exit("{0} is not a valid branch".format(branch))

    commits = get_commits(branch)

    if not commits:
        sys.exit("No commits to pick found")

    app = Application(commits)
    app.run()

    cherry_pick(app.picked)
