# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

from stgit import argparse
from stgit.argparse import opt
from stgit.commands import common
from stgit.lib import transaction
from stgit.out import out

__copyright__ = """
Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see http://www.gnu.org/licenses/.
"""

help = 'Permanently store the applied patches into the stack base'
kind = 'stack'
usage = ['',
         '[--] <patchnames>',
         '-n NUM',
         '--all']
description = """
Merge one or more patches into the base of the current stack and
remove them from the series while advancing the base. This is the
opposite of 'stg uncommit'. Use this command if you no longer want to
manage a patch with StGIT.

By default, the bottommost patch is committed. If patch names are
given, the stack is rearranged so that those patches are at the
bottom, and then they are committed.

The -n/--number option specifies the number of applied patches to
commit (counting from the bottom of the stack). If -a/--all is given,
all applied patches are committed."""

args = [argparse.patch_range(argparse.applied_patches,
                             argparse.unapplied_patches)]
options = [
    opt('-n', '--number', type = 'int',
        short = 'Commit the specified number of patches'),
    opt('-a', '--all', action = 'store_true',
        short = 'Commit all applied patches')]

directory = common.DirectoryHasRepositoryLib()

def func(parser, options, args):
    """Commit a number of patches."""
    stack = directory.repository.current_stack
    args = common.parse_patches(args, list(stack.patchorder.all_visible))
    if len([x for x in [args, options.number is not None, options.all] if x]) > 1:
        parser.error('too many options')
    if args:
        patches = [pn for pn in stack.patchorder.all_visible if pn in args]
        bad = set(args) - set(patches)
        if bad:
            raise common.CmdException('Nonexistent or hidden patch names: %s'
                                      % ', '.join(sorted(bad)))
    elif options.number is not None:
        if options.number <= len(stack.patchorder.applied):
            patches = stack.patchorder.applied[:options.number]
        else:
            raise common.CmdException('There are not that many applied patches')
    elif options.all:
        patches = stack.patchorder.applied
    else:
        patches = stack.patchorder.applied[:1]
    if not patches:
        raise common.CmdException('No patches to commit')

    iw = stack.repository.default_iw
    def allow_conflicts(trans):
        # As long as the topmost patch stays where it is, it's OK to
        # run "stg commit" with conflicts in the index.
        return len(trans.applied) >= 1
    trans = transaction.StackTransaction(stack, 'commit',
                                         allow_conflicts = allow_conflicts)
    try:
        common_prefix = 0
        for i in range(min(len(stack.patchorder.applied), len(patches))):
            if stack.patchorder.applied[i] == patches[i]:
                common_prefix += 1
            else:
                break
        if common_prefix < len(patches):
            to_push = [pn for pn in stack.patchorder.applied[common_prefix:]
                       if pn not in patches[common_prefix:]]
            # this pops all the applied patches from common_prefix
            trans.pop_patches(lambda pn: pn in to_push)
            for pn in patches[common_prefix:]:
                trans.push_patch(pn, iw)
        else:
            to_push = []
        new_base = trans.patches[patches[-1]]
        for pn in patches:
            trans.patches[pn] = None
        trans.applied = [pn for pn in trans.applied if pn not in patches]
        trans.base = new_base
        out.info('Committed %d patch%s' % (len(patches),
                                           ['es', ''][len(patches) == 1]))
        for pn in to_push:
            trans.push_patch(pn, iw)
    except transaction.TransactionHalted:
        pass
    return trans.run(iw)
