#!/usr/bin/python -u
# vim: set fileencoding=utf-8 :
#
# (C) 2011 Guido Guenther <agx@sigxcpu.org>
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    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, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
"""manage patches in a patch queue"""

import errno
import re
import os
import shutil
import subprocess
import sys
from gbp.config import (GbpOptionParser, GbpOptionGroup)
from gbp.git import (GitRepositoryError, GitRepository)
from gbp.command_wrappers import (Command, GitCommand, RunAtCommand,
                                  GitBranch, CommandExecFailed)
from gbp.errors import GbpError
import gbp.log

PQ_BRANCH_PREFIX = "patch-queue/"
PATCH_DIR = "debian/patches/"
SERIES_FILE = PATCH_DIR + "series"

def is_pq_branch(branch):
    return [False, True][branch.startswith(PQ_BRANCH_PREFIX)]

def pq_branch_name(branch):
    """get the patch queue branch corresponding to branch"""
    if not is_pq_branch(branch):
        return PQ_BRANCH_PREFIX + branch

def pq_branch_base(pq_branch):
    """get the branch corresponding to the given patch queue branch"""
    if is_pq_branch(pq_branch):
        return pq_branch[len(PQ_BRANCH_PREFIX):]

def export_patches(repo, branch, options):
    patch_re = re.compile("[0-9]+-(?P<name>.+)")

    if is_pq_branch(branch):
        base = pq_branch_base(branch)
        gbp.log.info("On '%s', switching to '%s'" % (branch, base))
        branch = base
        repo.set_branch(branch)

    pq_branch = pq_branch_name(branch)
    try:
        shutil.rmtree(PATCH_DIR)
    except OSError, (e, msg):
        if e != errno.ENOENT:
            raise GbpError, "Failed to remove patch dir: %s" % msg
        else:
            gbp.log.debug("%s does not exist." % PATCH_DIR)

    patches = repo.format_patches(branch, pq_branch, PATCH_DIR)
    if patches:
        f = file(SERIES_FILE, 'w')
        gbp.log.info("Regenerating patch queue in '%s'." % PATCH_DIR)
        for patch in patches:
	    # delete the first line (from sha1) and last two lines (git version
	    # info) of the patch file
            Command("sed -i -e '1d' -e 'N;$!P;$!D;$d' %s" % patch, shell=True)()
            Command("sed -i -e 's/^-- \\n[0-9\.]+$//' %s" % patch, shell=True)()

            name = patch[len(PATCH_DIR):]
            if not options.patch_numbers:
                m = patch_re.match(name)
                if m:
                    name = m.group('name')
                    shutil.move(patch, os.path.join(PATCH_DIR, name))

            f.write(name + '\n')
        f.close()
        GitCommand('status')(['--', PATCH_DIR])
    else:
        gbp.log.info("No patches on '%s' - nothing to do." % pq_branch)


def get_maintainer():
    cmd = 'sed -n -e \"s/Maintainer: \\+\\(.*\\)/\\1/p\" debian/control'
    maintainer = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.readlines()[0].strip()
    gbp.log.debug("Maintainer: %s" % maintainer)
    return maintainer


def import_patches(repo, branch):
    if is_pq_branch(branch):
        gbp.log.err("Already on a patch-queue branch '%s' - doing nothing." % branch)
        raise GbpError
    else:
        pq_branch = pq_branch_name(branch)

    try:
        GitBranch()(pq_branch)
    except CommandExecFailed:
        raise GbpError, ("Cannot create patch-queue branch '%s'. Try 'rebase' instead."
                         % pq_branch)
    repo.set_branch(pq_branch)

    if not os.path.exists(SERIES_FILE):
        gbp.log.info("Found no series file at '%s'. No patches to add to patch-queue branch."
                     % SERIES_FILE)
        return

    maintainer = get_maintainer()
    RunAtCommand('git', ['quiltimport', '--author', maintainer],
                 extra_env={'QUILT_PATCHES': PATCH_DIR})()


def drop_pq(repo, branch):
    if is_pq_branch(branch):
        gbp.log.err("On a patch-queue branch, can't drop it.")
        raise GbpError
    else:
        pq_branch = pq_branch_name(branch)

    if repo.has_branch(pq_branch):
        repo.delete_branch(pq_branch)
        gbp.log.info("Dropped branch %s." % pq_branch)
    else:
        gbp.log.info("No patch queue branch found - doing nothing.")


def rebase_pq(repo, branch):
    if not is_pq_branch (branch):
        pq_branch = pq_branch_name(branch)
        gbp.log.info("Switching to '%s'" % pq_branch)
        repo.set_branch(pq_branch)
    else:
        gbp.log.info("Already on '%s'" % branch)

    GitCommand("rebase")([branch])


def main(argv):
    retval = 0

    parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='',
                             usage="%prog [options] action - maintain patches on a patch queue branch\n"
        "Actions:\n"
        "  export         export the patch queue associated to the current branch\n"
        "                 into a quilt patch series in debian/patches/ and update the\n"
        "                 series file.\n"
        "  import         create a patch queue branch from quilt patches in debian/patches.\n"
        "  rebase         switch to patch queue branch associated to the current\n"
        "                 branch and rebase against current branch.\n"
        "  drop           drop (delete) the patch queue associated to the current branch.")
    parser.add_boolean_config_file_option(option_name="patch-numbers", dest="patch_numbers")
    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
                      help="verbose command execution")
    parser.add_config_file_option(option_name="color", dest="color", type='tristate')


    (options, args) = parser.parse_args(argv)
    gbp.log.setup(options.color, options.verbose)

    if len(args) != 2:
        gbp.log.err("No action given.")
        return 1
    elif args[1] not in ["export", "import", "rebase", "drop"]:
        gbp.log.err("Unknown action '%s'." % args[1])
        return 1
    else:
        action = args[1]

    try:
        repo = GitRepository(os.path.curdir)
    except GitRepositoryError:
        gbp.log.err("%s is not a git repository" % (os.path.abspath('.')))
        return 1

    try:
        current = repo.get_branch()
        if action == "export":
            export_patches(repo, current, options)
        elif action == "import":
            import_patches(repo, current)
        elif action == "drop":
            drop_pq(repo, current)
        elif action == "rebase":
            rebase_pq(repo, current)
    except CommandExecFailed:
        retval = 1
    except GbpError, err:
        if len(err.__str__()):
            gbp.log.err(err)
        retval = 1

    return retval

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

