#!/usr/bin/python -u
# vim: set fileencoding=utf-8 :
#
# (C) 2006,2007 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
"""Import a Debian source package into a git repository"""

import ConfigParser
import sys
import re
import os
import tempfile
import glob
import pipes
from email.Utils import parseaddr
import gbp.command_wrappers as gbpc
from gbp.deb import (debian_version_chars, parse_changelog, unpack_orig,
                     parse_dsc, DscFile, tar_toplevel)
from gbp.git import (build_tag, create_repo, GitRepository,
                     GitRepositoryError, rfc822_date_to_git)
from gbp.config import GbpOptionParser, GbpOptionGroup
from gbp.errors import GbpError


def git_apply_patch(diff):
    "Import patch via git apply"
    pipe = pipes.Template()
    pipe.prepend('gunzip -c %s' % diff,  '.-')
    pipe.append('git apply --index --apply --whitespace=nowarn -', '-.')
    try:
        ret = pipe.copy('', '')
        if ret:
            print >>sys.stderr, "Error import %s: %d" % (diff, ret)
            return False
    except OSError, err:
        print >>sys.stderr, "Error importing %s: %s" % (diff, err[0])
        return False
    return True


def apply_debian_patch(repo, src, options):
    """apply the debian patch and tag appropriately"""
    version = "%s-%s" % (src.upstream_version, src.debian_version)
    gitTag = gbpc.GitTag(options.sign_tags, options.keyid)
    try:
        if src.diff and not git_apply_patch(src.diff):
            raise GbpError
        os.chmod('debian/rules', 0755)
        if not repo.is_clean()[0]:
            dch = parse_changelog('debian/changelog')
            env = { 'GIT_AUTHOR_DATE': rfc822_date_to_git(dch['Date']) }

            name, addr = parseaddr(dch['Maintainer'])
            if name and addr:
                env['GIT_AUTHOR_NAME'] = name
                env['GIT_AUTHOR_EMAIL'] = addr
            else:
                print >>sys.stderr, "Warning: failed to parse maintainer"
            gbpc.GitCommitAll(extra_env=env)(msg="Imported Debian patch %s" % version)
        else:
            print "Nothing to commit, nothing imported."
        gitTag(build_tag(options.debian_tag, version),
               msg="Debian release %s" % version)
    except gbpc.CommandExecFailed:
        print >>sys.stderr, "Failed to import Debian package"
        raise GbpError


def print_dsc(dsc):
    if dsc.native:
        print "Debian Native Package"
        print "Version:", dsc.upstream_version
    else:
        print "Upstream version:", dsc.upstream_version
        print "Debian version:", dsc.debian_version
    if dsc.epoch:
        print "Epoch: %s" % dsc.epoch


def main(argv):
    dirs = {'top': os.path.abspath(os.curdir)}
    needs_repo = False
    ret = 0

    try:
        parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='',
                                 usage='%prog [options] /path/to/package.dsc')
    except ConfigParser.ParsingError, err:
        print >>sys.stderr, err
        return 1

    import_group = GbpOptionGroup(parser, "import options",
                      "pristine-tar and filtering")
    tag_group = GbpOptionGroup(parser, "tag options",
                      "options related to git tag creation")
    branch_group = GbpOptionGroup(parser, "version and branch naming options",
                      "version number and branch layout options")

    for group in [import_group, branch_group, tag_group ]:
        parser.add_option_group(group)

    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
                      help="verbose command execution")
    branch_group.add_config_file_option(option_name="debian-branch",
                      dest="debian_branch")
    branch_group.add_config_file_option(option_name="jolicloud-branch",
                      dest="jolicloud_branch")
    branch_group.add_config_file_option(option_name="upstream-branch",
                      dest="upstream_branch")
    branch_group.add_option("--no-merge", dest='merge', action="store_false",
                      default=True,
                      help="after import dont do any merging to another branch")

    tag_group.add_boolean_config_file_option(option_name="sign-tags",
                      dest="sign_tags")
    tag_group.add_config_file_option(option_name="keyid",
                      dest="keyid")
    tag_group.add_config_file_option(option_name="debian-tag",
                      dest="debian_tag")
    tag_group.add_config_file_option(option_name="jolicloud-tag",
                      dest="jolicloud_tag")
    tag_group.add_config_file_option(option_name="upstream-tag",
                      dest="upstream_tag")

    import_group.add_config_file_option(option_name="filter",
                      dest="filters", action="append")
    import_group.add_boolean_config_file_option(option_name="pristine-tar",
                      dest="pristine_tar")
    (options, args) = parser.parse_args(argv[1:])

    if options.verbose:
        gbpc.Command.verbose = True

    gitTag = gbpc.GitTag(options.sign_tags, options.keyid)

    try:
        if len(args) != 1:
            parser.print_help()
            raise GbpError
        else:
            src = parse_dsc(args[0])
            if options.verbose:
                print_dsc(src)

            try:
                repo = GitRepository('.')
                is_empty = repo.is_empty()

                (clean, out) = repo.is_clean()
                if not clean and not is_empty:
                    print >>sys.stderr, "Repository has uncommitted changes, commit these first: "
                    raise GbpError, out

            except GitRepositoryError:
                # no repo found, create one
                needs_repo = True
                is_empty = True

            if needs_repo:
                print "No git repository found, creating one."
                repo = create_repo(src.pkg)
                os.chdir(repo.path)

            dirs['tmp'] = os.path.abspath(tempfile.mkdtemp(dir='..'))
            unpack_dir = unpack_orig(src.tgz, dirs['tmp'], options.filters)
            unpack_dir = tar_toplevel(unpack_dir)

            format = [(options.upstream_tag, "Upstream"), (options.debian_tag, "Debian")][src.native]
            tag = build_tag(format[0], src.upstream_version)
            msg = "%s version %s" % (format[1], src.upstream_version)

            if not repo.has_tag(tag):
                print "tag %s not found, importing %s tarball" % (tag, format[1])
                # FIXME: this is what import-orig does - merge
                if not is_empty:
                    if src.native:
                        repo.set_branch(options.debian_branch)
                    else:
                        repo.set_branch(options.upstream_branch)
                repo.replace_tree(unpack_dir, options.filters, verbose=True)
                gbpc.GitCommitAll()(msg="Imported %s" % msg)
                gitTag(tag, msg=msg)
                if not src.native and is_empty:
                    gbpc.GitBranch()(options.upstream_branch)
                if options.pristine_tar and not src.native:
                    gbpc.PristineTar().commit(src.tgz, 'refs/heads/%s' % options.upstream_branch)
            if not src.native:
                if is_empty and not repo.has_branch(options.debian_branch):
                    gbpc.GitBranch()(options.debian_branch)
                repo.set_branch(options.debian_branch)
                if options.merge:
                    print "Merging to %s" % options.debian_branch
                    try:
                        gbpc.GitMerge(options.upstream_branch)()
                    except gbpc.CommandExecFailed:
                        raise GbpError, """Merge of %s failed, please resolve manually""" % options.upstream_branch
                repo.replace_tree(unpack_dir, options.filters)
                apply_debian_patch(repo, src, options)
            repo.set_branch(options.jolicloud_branch)
            if options.merge:
                print "Merging to %s" % options.jolicloud_branch
                try:
                    gbpc.GitMerge(options.debian_branch)()
                except gbpc.CommandExecFailed:
                    raise GbpError, """Merge of %s failed, please resolve manually""" % options.debian_branch
    except gbpc.CommandExecFailed:
        os.chdir(dirs['top'])
        ret = 1
    except GbpError, err:
        if len(err.__str__()):
            print >>sys.stderr, err
        os.chdir(dirs['top'])
        ret = 1

    if dirs.has_key('tmp'):
        gbpc.RemoveTree(dirs['tmp'])()

    if not ret:
        print "Everything imported under '%s'" % src.pkg
    return ret

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

# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
