#!/usr/bin/python
#
# pull-debian-source -- pull a source package from Launchpad
# Copyright (C) 2011, Stefano Rivera <stefanor@ubuntu.com>
# Inspired by a tool of the same name by Nathan Handler.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

import json
import optparse
import sys
import urllib2

from distro_info import DebianDistroInfo, DistroDataOutdated

from ubuntutools.archive import DebianSourcePackage, DownloadError, rmadison
from ubuntutools.config import UDTConfig
from ubuntutools.logger import Logger


def is_suite(version):
    """If version could be considered to be a Debian suite, return the
    canonical suite name. Otherwise None
    """
    debian_info = DebianDistroInfo()
    debian_releases = debian_info.all + ['experimental']

    if '-' in version:
        release, pocket = version.split('-', 1)
        release = debian_info.codename(release, default=release)
        if release in debian_releases:
            if pocket in ('proposed-updates', 'p-u'):
                return (release + '-proposed-updates')
            elif pocket == 'security':
                return (release + '-security')
    else:
        release = debian_info.codename(version, default=version)
        if release in debian_releases:
            return release
    return None


def source_package_for(binary, release):
    """Query DDE to find the source package for a particular binary"""
    try:
        release = DebianDistroInfo().codename(release, default=release)
    except DistroDataOutdated, e:
        Logger.warn(e)
    url = ('http://dde.debian.net/dde/q/udd/dist/d:debian/r:%s/p:%s/?t=json'
           % (release, binary))
    data = None
    try:
        data = json.load(urllib2.urlopen(url))['r']
    except urllib2.URLError, e:
        Logger.error('Unable to retrieve package information from DDE: '
                     '%s (%s)', url, str(e))
    except ValueError, e:
        Logger.error('Unable to parse JSON response from DDE: '
                     '%s (%s)', url, str(e))
    if not data:
        return None
    return data[0]['source']


def main():
    usage = 'Usage: %prog <package> [release|version]'
    parser = optparse.OptionParser(usage)
    parser.add_option('-d', '--download-only',
                      dest='download_only', default=False, action='store_true',
                      help='Do not extract the source package')
    parser.add_option('-m', '--mirror', metavar='DEBIAN_MIRROR',
                      dest='debian_mirror',
                      help='Preferred Debian mirror (default: %s)'
                           % UDTConfig.defaults['DEBIAN_MIRROR'])
    parser.add_option('-s', '--security-mirror', metavar='DEBSEC_MIRROR',
                      dest='debsec_mirror',
                      help='Preferred Debian Security mirror (default: %s)'
                           % UDTConfig.defaults['DEBSEC_MIRROR'])
    parser.add_option('--no-conf',
                      dest='no_conf', default=False, action='store_true',
                      help="Don't read config files or environment variables")
    (options, args) = parser.parse_args()
    if not args:
        parser.error('Must specify package name')
    elif len(args) > 2:
        parser.error('Too many arguments. '
                     'Must only specify package and (optionally) release.')

    config = UDTConfig(options.no_conf)
    if options.debian_mirror is None:
        options.debian_mirror = config.get_value('DEBIAN_MIRROR')
    if options.debsec_mirror is None:
        options.debsec_mirror = config.get_value('DEBSEC_MIRROR')

    package = args[0].lower()

    version = args[1] if len(args) > 1 else 'unstable'
    component = None

    suite = is_suite(version)
    if suite is not None:
        line = list(rmadison('debian', package, suite, 'source'))
        if not line:
            source_package = source_package_for(package, suite)
            if source_package != None and package != source_package:
                package = source_package
                line = list(rmadison('debian', package, suite, 'source'))
            if not line:
                Logger.error('Unable to find %s in Debian suite "%s".', package,
                             suite)
                sys.exit(1)
        line = line[-1]
        version = line['version']
        component = line['component']

    Logger.normal('Downloading %s version %s', package, version)
    srcpkg = DebianSourcePackage(package, version, component=component,
                                 mirrors=[options.debian_mirror,
                                          options.debsec_mirror])
    try:
        srcpkg.pull()
    except DownloadError, e:
        Logger.error('Failed to download: %s', str(e))
        sys.exit(1)
    if not options.download_only:
        srcpkg.unpack()

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        Logger.normal('User abort.')