#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2006-2007 (C) Pete Savage <petesavage@ubuntu.com>
# Copyright 2007 (C) Siegfried-A. Gevatter <rainct@ubuntu.com>
# Copyright 2009 (C) Canonical Ltd. (by Colin Watson <cjwatson@ubuntu.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; 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.
#
# See file /usr/share/common-licenses/GPL for more details.
#
# ##################################################################
#
# This script is used to check if a package and all its build
# dependencies are in main or not.

import sys

import apt_pkg
import apt

from ubuntutools import subprocess


def process_deps(cache, deps):
    """Takes a list of (build) dependencies and processes it."""

    for basedep in [d.or_dependencies[0] for d in deps]:
        if basedep.name not in packages and basedep.name != '':
            # Check the (build) dependencies recursively
            find_main(cache, basedep.name)


def get_package_version(cache, distro, pack):
    if pack not in cache:
        return None
    for version in (cache[pack].candidate, cache[pack].installed):
        if not version:
            continue
        for origin in version.origins:
            if origin.archive == distro:
                return version
    return None


# Cache::CompTypeDeb isn't exposed via python-apt
def comp_type_deb(op):
    ops = ("", "<=", ">=", "<<", ">>", "=", "!=")
    if (op & 15) < 7:
        return ops[op & 15]
    return ""


def find_main(cache, pack):
    """Searches the dependencies and build dependencies of a package recursively
    to determine if they are all in the 'main' component or not."""

    global packages

    if pack in packages:
        return

    # Retrieve information about the package
    version = get_package_version(cache, distro, pack)

    if not version:
        packages[pack] = False
        return
    elif [origin for origin in version.origins if origin.component == 'main']:
        packages[pack] = True
        return
    else:
        if pack not in packages:
            packages[pack] = False

        # Retrieve package dependencies
        process_deps(cache, version.dependencies)

        # Retrieve package build dependencies. There's no handy
        # attribute on version for this, so unfortunately we have to
        # do a lot of messing about with apt.
        deps = []
        src_records = apt_pkg.SourceRecords()
        got_src = False
        while src_records.lookup(version.source_name):
            if pack in src_records.binaries:
                got_src = True
                break
        if got_src:
            # pylint: disable=E1101
            for _, all_deps in src_records.build_depends.iteritems():
                # pylint: enable=E1101
                for or_deps in all_deps:
                    base_deps = []
                    for (name, ver, op) in or_deps:
                        # pylint: disable=too-many-function-args
                        base_deps.append(apt.package.BaseDependency(name, op,
                                                                    ver, False))
                        # pylint: enable=too-many-function-args
                    # pylint: disable=no-value-for-parameter
                    deps.append(apt.package.Dependency(base_deps))
                    # pylint: enable=no-value-for-parameter

        process_deps(cache, deps)


def usage(exit_code):
    print 'Usage: %s <package name> [<distribution>]' % sys.argv[0]
    sys.exit(exit_code)


def main():

    global packages, distro

    # Check if the amount of arguments is correct
    if len(sys.argv) > 1 and sys.argv[1] in ('help', '-h', '--help'):
        usage(0)

    if len(sys.argv) < 2 or len(sys.argv) > 3:
        usage(1)

    cache = apt.cache.Cache()

    if len(sys.argv) == 3 and sys.argv[2]:
        distro = sys.argv[2]
        if not get_package_version(cache, distro, 'bash'):
            print u'«%s» is not a valid distribution.' % distro
            print('Remember that for 404main to work with a certain distribution '
                  'it must be in your /etc/apt/sources.list file.')
            sys.exit(1)
    else:
        cmd = ['lsb_release', '-cs']
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        distro = process.stdout.read().strip('\n')

    if not get_package_version(cache, distro, sys.argv[1]):
        print(u"Can't find package «%s» in distribution «%s»." % (sys.argv[1], distro))
        sys.exit(1)

    print(u'Checking package «%s» in distribution «%s»...' % (sys.argv[1], distro))

    find_main(cache, sys.argv[1])

    # True if everything checked until the point is in main
    all_in_main = True

    for package in packages:
        if not packages[package]:
            if all_in_main:
                print 'The following packages aren\'t in main:'
                all_in_main = False
            print '  ', package

    if all_in_main:
        print((u'Package «%s» and all its dependencies and build dependencies are in main.') %
              sys.argv[1])


if __name__ == '__main__':

    # Global variable to hold the status of all packages
    packages = {}

    # Global variable to hold the target distribution
    distro = ''

    try:
        main()
    except KeyboardInterrupt:
        print 'Aborted.'
        sys.exit(1)