#!/usr/bin/python
# -*- coding: utf-8 -*-

# (C) 2008 Canonical Ltd., Authors
#
# Authors:
#   Martin Pitt <martin.pitt@ubuntu.com>
#   Steve Kowalik <stevenk@ubuntu.com>
#   Michael Bienia <geser@ubuntu.com>
#   Daniel Hahler <ubuntu@thequod.de>
#   Iain Lane <iain@orangesquash.org.uk>
#   Jonathan Patrick Davies <jpds@ubuntu.com>
#   Bryce Harrington <bryce@canonical.com>
#
# ###############################################################
#
# 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; version 2 or newer.
#
# 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.
#
# See file /usr/share/common-licenses/GPL-2 for more details.
#
# ###############################################################

# This is a simplified version of the requestsync script from
# ubuntu-dev-tools, that uses launchpadlib (via Arsenal) as the
# backend.

from arsenal.arsenal_lib import *
from lpltk import LaunchpadService
import getopt
import subprocess
import urllib
import urllib2
from debian_bundle.changelog import Version

def usage():
    print '''Usage:  syncreq [-h|-d sync-version|-e|-v base-version] [rationale] <source-package>

Files sync requests for a source package.

In some cases, the base version (fork point from Debian) cannot be determined
automatically, and you will get a complete Debian changelog. Specify the correct
base version of the package in Ubuntu using the -v flag.

Also see the requestsync script in the ubuntu-dev-tools package.

Options:
        -h             Print this help
        -d <version>   Debian version to sync
        -e             Sync from Debian Experimental        
        -v <version>   Base version in Ubuntu

Stock Rationales:
        --all-changes-upstream
        --fakesync
        --no-change
'''
    sys.exit(1)

# TODO:  Generalize this and move it into Arsenal proper
#        It should return a data structure with all the collected info
def source_info(package, distro, release):
    '''Determine current package version in ubuntu or debian.'''
    print 'rmadison -u %s -a source -s %s %s' % (distro, release, package)
    madison = subprocess.Popen(['rmadison', '-u', distro, '-a', 'source', \
                                '-s', release, package], stdout=subprocess.PIPE)
    out = madison.communicate()[0]
    assert (madison.returncode == 0)

    try:
        assert out
    except AssertionError:
        print "%s doesn't appear to exist in %s." % (package, distro)
        sys.exit(1)

    for l in out.splitlines():
        print "Received: ", l
        (pkg, version, rel, builds) = l.split('|')

        component = 'main'
        if rel.find('/') != -1:
            component = rel.split('/')[1]
        if builds.find('source') != -1:
            return (version.strip(), component.strip())

    print "%s doesn't appear to exist in %s, specify -n for a package not in Ubuntu." % (package, release)
    sys.exit(1)

# TODO:  Generalize and put into Arsenal
def debian_changelog(package, component, base_version):
    '''Return the Debian changelog from the latest up to the given version (exclusive).'''

    ch = ''
    subdir = package[0]

    if package.startswith('lib'):
        subdir = 'lib%s' % package[3]

    debian_changelog_url = 'http://packages.debian.org/changelogs/pool/%s/%s/%s/current/changelog.txt' % (component, subdir, package)
    try:
        debianChangelogPage = urllib2.urlopen(debian_changelog_url)
    except urllib2.HTTPError, error:
        print >> sys.stderr, "Unable to connect to packages.debian.org. " \
            "Received a %s." % error.code
        print >> sys.stderr, debian_changelog_url
        sys.exit(1)

    for l in debianChangelogPage:
        if l.startswith(package):
            ch_version = l[ l.find("(")+1 : l.find(")") ]
            if Version(ch_version) <= base_version:
                break
        ch += l

    return ch

if __name__ == '__main__':
    lp                   = LaunchpadService()
    d                    = lp.launchpad.distributions["ubuntu"]

    base_version         = None
    ubu_version          = '0'
    deb_series           = 'unstable'
    deb_version          = None
    rationale            = None

    try:
        opts, args = getopt.gnu_getopt(sys.argv[1:], 'hd:ev:', ('all-changes-upstream', 'fakesync', 'no-change'))
    except getopt.GetoptError:
        usage()
    for o, a in opts:
        if o == '-h':
            usage()
            sys.exit()
        if o == '-d':   deb_version = a
        if o == '-e':   deb_series = 'experimental'
        if o == '-v':   base_version = a
        if o == '--all-changes-upstream':
            rationale = "\n  * All ubuntu changes are present in the upstream release\n\n"
        if o == '--fakesync':
            rationale = "\n  * The prior ubuntu version was only due to need for a fakesync\n\n"
        if o == '--no-change':
            rationale = "\n  * The prior ubuntu version was a rebuild; there were no source changes\n\n"

    # TODO: permit specifying multiple packages to sync

    if len(args) < 1:
        usage()
    elif base_version and len(args) > 1:
        print "Error:  Cannot use -v option with more than one source-package argument\n"
        usage()

    # TODO:  Implement requestsync's checkExistingReports() using launchpadlib

    # TODO:  Move this into arsenal
    # Determine the current development target
    ubu_series = None
    num_development = 0
    ubuntu = lp.launchpad.distributions["ubuntu"]
    for series in ubuntu.series:
        if series.status == "Active Development" or series.status == "Pre-release Freeze":
            num_development += 1
            ubu_series = series.name

    if num_development > 1:
        # TODO:  Provide cmdline option for this situation
        print "Error:  More than one release series is in development.  Can't decide which to use."
        sys.exit(1)
    elif num_development < 1:
        print "Error:  No release series in development"
        sys.exit(1)

    if not ubu_series:
        print "Error:  Could not determine release"
        sys.exit(1)

    # TODO:  Check whether we're pre-feature-freeze or not
    #ubuntu.active_milestones

    package = args[0]
    (ubu_version, ubu_component) = source_info(package, 'ubuntu', ubu_series)

    # TODO:  If qa.debian.org is down, this fails...
    if deb_version == None:
        (deb_version, deb_component) = source_info(package, 'debian', deb_series)
    else:
        # Wildly guess it's in main
        deb_component = 'main'

    report                       = unicode("Please sync this package from debian to ubuntu\n", "utf-8")

    if ubu_version == deb_version:
        print 'The versions in Debian and Ubuntu are the same already (%s).' % (deb_version)
        sys.exit(0)

    if not base_version:
        base_version = Version(ubu_version)

    # TODO:  Detect if there are ubuntu changes, and prompt to explain why they can be dropped
    if rationale:
        report += rationale

    # TODO:  Determine if we're post-UVF, and if so prepend UVFe
    title   = "[Sync Request] Please sync %s (%s) from Debian [%s] (%s)" % (package, deb_version, deb_series, ubu_component)
    print title
    report += 'Changelog since current %s version %s:\n\n' % (ubu_series, ubu_version)
    try:
        report += unicode(debian_changelog(package, deb_component, base_version), 'utf-8', errors='ignore')
    except:
        report = ""
        print "ERROR:  Invalid changelog for " + package + " " + ubu_version
        raise

    # TODO:  Permit editing the report before sending it?

    #        print report.encode('utf-8') + "\n\n"
    print "Filing bug against %s: %s" %(package, title)
    print report
    d = lp.launchpad.distributions["ubuntu"]
    target = d.getSourcePackage(name = package)
    bug = lp.launchpad.bugs.createBug(title = title, description = report, target = target)
    print 'Sync request filed as bug #%i: https://launchpad.net/bugs/%i' % (bug.id, bug.id)

    bug.bug_tasks[0].status = "Confirmed"
    bug.bug_tasks[0].importance = "Wishlist"
    bug.bug_tasks[0].lp_save()
    person = lp.launchpad.people['ubuntu-archive']
    bug.subscribe(person = person)
    print " * %s: sync req# %d" % (package, bug.id)


# TODO:
# rmadison -u ubuntu -a source -s jaunty xserver-xorg-video-mga
# rmadison -u debian -a source -s experimental xserver-xorg-video-mga
# [Sync Request] Please sync xserver-xorg-video-mga (1:1.4.9.dfsg-2) from Debian [experimental] (main)
# Traceback (most recent call last):
#      File "./syncreq", line 191, in <module>
#          report += debian_changelog(package, deb_component, base_version).encode('utf-8') + "\n"
#          UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4378: ordinal not in range(128)
# Rerunning script avoided the error the second time
