#!python
from gitz.git import GIT
from gitz.git import delete
from gitz.git import functions
from gitz.git import guess_origin
from gitz.git import root
from gitz.program import ARGS
from gitz.program import PROGRAM

SUMMARY = 'Push a sequence of commit IDs to a remote repository'

HELP = """
Starting with a given commit ID, and moving backwards from there,
push each commit ID to its own disposable branch name.

Useful to bring these commits to the attention of your continuous integration
if it has missed some of your commit IDs because you rebased or pushed a
sequences of commits too fast.
"""

PREFIX = '_gitz_stripe_'
BAD_BRANCH_CHARS = frozenset('~^: ')


def git_stripe():
    root.check_git()
    Stripe().stripe()


class Stripe:
    def __init__(self):
        self.remote_branches = functions.remote_branches()
        self.remotes = list(self._remotes())

    def stripe(self):
        if ARGS.delete:
            self._delete()
            return

        if ARGS.list:
            self._list()
            return

        self._check_overwrite()
        commits = functions.commit_ids(ARGS.commits or ['HEAD~'])
        for i, commit in enumerate(commits):
            for j in range(ARGS.count):
                self._create_one(i, j, commit)

    def _create_one(self, i, j, commit):
        index = ARGS.offset + ARGS.count * i + j
        branch = '%s%d' % (PREFIX, index)
        refspec = '%s~%d:refs/heads/%s' % (commit, j, branch)

        id = ''
        for remote in self.remotes:
            GIT.push('--force-with-lease', remote, refspec)
            if not id:
                striped = '%s/%s' % (remote, branch)
                id, msg = functions.commit_message(striped)

        PROGRAM.log.message('+ %s@%s: %s' % (striped, id, msg))

    def _check_overwrite(self):
        if ARGS.safe:
            branches = {PREFIX + str(i) for i in self.indexes}
            remote_branches = set()
            for remote in self.remotes:
                remote_branches.update(self.remote_branches[remote])

            existing = sorted(branches.intersection(remote_branches))
            if existing:
                PROGRAM.exit('Cannot overwrite existing', *existing)

    def _remotes(self):
        for remote in ARGS.remotes.split(':'):
            if remote == '^':
                yield guess_origin.guess_origin()
            elif remote == '.' or remote in self.remote_branches:
                yield remote
            else:
                PROGRAM.exit('Unknown remote', remote)

    def _delete(self):
        if ARGS.commits:
            PROGRAM.exit(_ERROR_DELETE_ARGS)

        if ARGS.count != 1:
            PROGRAM.exit(_ERROR_DELETE_COUNT)

        for remote in self.remotes:
            for branch in self.remote_branches[remote]:
                if branch.startswith(PREFIX):
                    delete.delete_remote_branch(remote, branch)

    def _list(self):
        for remote in self.remotes:
            for branch in self.remote_branches[remote]:
                if branch.startswith(PREFIX):
                    rbranch = '%s/%s' % (remote, branch)
                    id, msg = functions.commit_message(rbranch)
                    PROGRAM.log.message('%s@%s: %s' % (branch, id, msg))


def add_arguments(parser):
    add = parser.add_argument

    add('commits', nargs='*', help=_HELP_COMMITS)
    add('-c', '--count', default=1, type=int, help=_HELP_COUNT)
    add('-d', '--delete', action='store_true', help=_HELP_DELETE)
    add('-l', '--list', action='store_true', help=_HELP_LIST)
    add('-o', '--offset', default=0, type=int, help=_HELP_OFFSET)
    add('-r', '--remotes', default='^', help=_HELP_REMOTE)
    add('-s', '--safe', action='store_true', help=_HELP_SAFE)


_ERROR_BRANCH_NAME = 'Illegal character in branch name'
_ERROR_DELETE_ARGS = 'git stripe -d takes no arguments'
_ERROR_DELETE_COUNT = 'git stripe -d ignores --count/-c'

_HELP_COMMITS = 'Branch/commit IDs to be striped (defaults to HEAD~)'
_HELP_COUNT = 'The number of striped branches to be created'
_HELP_DELETE = 'Delete all striped branches'
_HELP_LIST = 'List all remote stripes'
_HELP_OFFSET = 'Offset to start numbering stripes'
_HELP_REMOTE = (
    'One or more remote remotes to push to, separated by colon. '
    '  "." means the local repo, "^" means the upstream repo'
)
_HELP_SAFE = 'Do not force push over existing stripes'

EXAMPLES = """
git stripe
    Pushes HEAD~ into its own branch named _gitz_stripe_0

git stripe --count=3
git stripe -c3
    Pushes HEAD~, HEAD~2 and HEAD~3 into their own branches named
    _gitz_stripe_0, _gitz_stripe_1 and _gitz_stripe_2

git stripe --offset=5
git stripe -o5
    Pushes HEAD~, HEAD~2 and HEAD~3 into their own branches named
    _gitz_stripe_5, _gitz_stripe_6 and _gitz_stripe_7

git stripe 2 HEAD~3
git stripe HEAD~3 2
    Pushes HEAD~3 and HEAD~4 into two branches named _gitz_stripe_0
    and  _gitz_stripe_1

git stripe --delete-all
git stripe -D
    Delete all stripes
"""

if __name__ == '__main__':
    PROGRAM.start()
