#!/usr/bin/python
# 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
"""make a git archive out of a Debian source package"""

import sys
import re
import os
import tempfile
import glob
import gbp.command_wrappers as gbpc
from gbp.deb_utils import debian_version_chars
from gbp.git_utils import build_tag, GitRepository
from gbp.config import GbpOptionParser
from gbp.errors import GbpError


class DscFile(object):
    """Keeps all needed data read from a dscfile"""
    pkg_re = re.compile('Source:\s+(?P<pkg>.+)\s*')
    version_re = re.compile("Version:\s(\d+\:)?(?P<version>[%s]+)\s*$" % debian_version_chars)
    tar_re = re.compile('^\s\w+\s\d+\s+(?P<tar>[^_]+_[^_]+(\.orig)?\.tar\.(gz|bz2))')

    def __init__(self, dscfile):
        self.dscfile = os.path.abspath(dscfile)
        f = file(self.dscfile)
        for line in f:
            m = self.version_re.match(line)
            if m:
                if '-' in m.group('version'):
                    self.debian_version = m.group('version').split("-")[-1]
                    self.upstream_version = "-".join(m.group('version').split("-")[0:-1])
                    self.native = False
                else:
                    self.native = True # Debian native package
                    self.upstream_version = m.group('version')
                continue
            m = self.pkg_re.match(line)
            if m:
                self.pkg = m.group('pkg')
                continue
            m = self.tar_re.match(line)
            if m:
                fromdir = os.path.dirname(dscfile)
                self.tgz = os.path.join(fromdir, m.group('tar'))
                continue
        f.close()


def parse_dsc(dscfile):
    """parse dsc by creating a DscFile object"""
    try:
        dsc = DscFile(dscfile)
    except IOError, err:
        print >>sys.stderr, "Error reading dsc file: %s" % err
        dsc = None
    else:
        try:
            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
        except AttributeError:
            print >>sys.stderr, "Error parsing dsc file %s" % dscfile 
            dsc = None
    return dsc


def import_initial(src, dirs, options, tagger, filter):
    """
    import the intial version and (in the case of a non native package) create
    the 'upstream' branch. Tag everything appropriately.
    """
    try:
        unpackTGZ = gbpc.UnpackTarArchive(src.tgz, dirs['tmp'], filter=filter)
        unpackTGZ()
    except gbpc.CommandExecFailed:
        print >>sys.stderr, "Unpacking of %s failed" % src.tgz
        gbpc.RemoveTree(dirs['tmp'])()
        return False

    try:
        dirs['git'] = glob.glob('%s/*' % unpackTGZ.dir)[0]
        os.chdir(dirs['git'])
        gbpc.GitInitDB()()
        gbpc.GitAdd()(['.'])
        gbpc.GitCommitAll()(
             msg="Imported %s version %s" % (['upstream', 'Debian'][src.native],
             src.upstream_version))
        format = [options.upstream_tag, options.debian_tag][src.native]
        tagger(build_tag(format, src.upstream_version),
               msg="Upstream version %s" % src.upstream_version)

        if not src.native:
            gbpc.GitBranch()(options.upstream_branch)
            if options.pristine_tar:
                gbpc.PristineTar().commit(os.path.join(dirs['top'], src.tgz),
                                          options.upstream_branch)

    except gbpc.CommandExecFailed:
        print >>sys.stderr, "Creation of git repository failed"
        gbpc.RemoveTree(unpackTGZ.dir)()
        return False
    return True


def apply_debian_patch(src, dirs, options, tagger, filter):
    """apply the debian patch and tag appropriately"""
    try:
        version = "%s-%s" % (src.upstream_version, src.debian_version)
        gbpc.DpkgSourceExtract()(src.dscfile, dirs['dpkg-src'])
        os.chdir(dirs['git'])
        repo = GitRepository('.')

        old = set(repo.index_files())
        new = set(gbpc.copy_from(dirs['dpkg-src'], filter))
        gbpc.GitAdd()(['.'])
        files = [ obj for obj in old - new if not os.path.isdir(obj)]
        if files:
            gbpc.GitRm()(files)
        if not repo.is_clean()[0]:
            gbpc.GitCommitAll()(msg="Imported Debian patch %s" % version)
        else:
            print "Nothing to commit, nothing imported."

        tagger(build_tag(options.debian_tag, version),
               msg="Debian release %s" % version)
    except gbpc.CommandExecFailed:
        print >>sys.stderr, "Failed to import Debian package"
        return False
    return True


def move_tree(src, dirs):
    """rename the temporary unpack directory to it's final name"""
    try:
        os.rename(dirs['git'], src.pkg)
    except OSError, err:
        print >>sys.stderr, "Cannot move git repository '%s' to it's final location '%s': %s" % (dirs['git'],
                             os.path.abspath(src.pkg), err)
        return False
    else:
        gbpc.RemoveTree(dirs['tmp'])()
    return True


def create_debian_branch(debian_branch, dirs):
    os.chdir(dirs['git'])
    repo = GitRepository('.')
    if repo.get_branch() != debian_branch:
        if not repo.has_branch(debian_branch):
            print "Creating Debian branch '%s'" % debian_branch
            gbpc.GitBranch()(debian_branch)
        gbpc.GitCheckoutBranch(debian_branch)
    os.chdir(dirs['top'])


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

    parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='',
                             usage='%prog [options] /path/to/package.dsc')

    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
                      help="verbose command execution")
    parser.add_config_file_option(option_name="debian-branch", dest='debian_branch',
                      help="branch the debian patch is being developed on, default is '%(debian-branch)s'")
    parser.add_config_file_option(option_name="upstream-branch", dest="upstream_branch",
                      help="upstream branch, default is '%(upstream-branch)s'")
    parser.add_config_file_option(option_name="sign-tags", dest="sign_tags",
                      help="sign git tags", action="store_true")
    parser.add_config_file_option(option_name="keyid", dest="keyid",
                      help="GPG keyid to sign tags with")
    parser.add_config_file_option(option_name="debian-tag", dest="debian_tag",
                      help="Format string for debian tags, default is '%(debian-tag)s'")
    parser.add_config_file_option(option_name="upstream-tag", dest="upstream_tag",
                      help="Format string for upstream tags, default is '%(upstream-tag)s'")
    parser.add_config_file_option(option_name="filter", dest="filter",
                      help="files to filter out during import")
    parser.add_config_file_option(option_name="pristine-tar", dest="pristine_tar",
                      help="Use pristine-tar to import the tarball", action="store_true")
    (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 not src:
                raise GbpError

            dirs['tmp'] = os.path.abspath(tempfile.mkdtemp(dir='.'))
            if not import_initial(src, dirs, options, gitTag, options.filter):
                raise GbpError

            os.chdir(dirs['top'])
            if not src.native:
                dirs['unpack'] = os.path.join(dirs['tmp'], 'unpack')
                os.mkdir(dirs['unpack'])
                dirs['dpkg-src'] = os.path.join(dirs['unpack'],
                                                "%s-%s-%s" % (src.pkg, src.upstream_version, src.debian_version))
                if not apply_debian_patch(src, dirs, options, gitTag, options.filter):
                    raise GbpError
                create_debian_branch(options.debian_branch, dirs)
            os.chdir(dirs['top'])
            if not move_tree(src, dirs):
                raise GbpError
    except GbpError, err:
        if len(err.__str__()):
            print >>sys.stderr, err
        ret = 1
        os.chdir(dirs['top'])

    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\:·:
