# -*- coding: utf-8 -*-
"""

    @author: Fabio Erculiani <lxnay@sabayon.org>
    @contact: lxnay@sabayon.org
    @copyright: Fabio Erculiani
    @license: GPL-2

    B{Entropy Infrastructure Toolkit}.

"""
import sys
import argparse

from entropy.output import purple, darkgreen, bold, brown, teal
from entropy.const import etpConst
from entropy.i18n import _

from eit.commands.descriptor import EitCommandDescriptor
from eit.commands.command import EitCommand
from eit.utils import print_package_info

class EitQuery(EitCommand):
    """
    Main Eit query command.
    """

    NAME = "query"
    ALIASES = ["q"]
    ALLOW_UNPRIVILEGED = True

    def __init__(self, args):
        EitCommand.__init__(self, args)
        self._nsargs = None
        self._quiet = False
        self._verbose = False
        self._repository_id = None

    def _get_parser(self):
        """ Overridden from EitCommand """
        descriptor = EitCommandDescriptor.obtain_descriptor(
            EitQuery.NAME)
        parser = argparse.ArgumentParser(
            description=descriptor.get_description(),
            formatter_class=argparse.RawDescriptionHelpFormatter,
            prog="%s %s" % (sys.argv[0], EitQuery.NAME))

        subparsers = parser.add_subparsers(
            title="action", description=_("execute query"),
            help=_("available queries"))

        tags_parser = subparsers.add_parser("tags",
            help=_("search package tags"))
        tags_parser.add_argument("--quiet", "-q", action="store_true",
            default=self._quiet,
            help=_('quiet output, for scripting purposes'))
        tags_parser.add_argument("--in", metavar="<repository>",
            help=_("query into given repository only"),
            dest="inrepo", default=None)
        tags_parser.add_argument("tags", nargs='+',
                                 metavar="<tag>",
                                 help=_("tag name"))
        tags_parser.set_defaults(func=self._tags)

        needed_parser = subparsers.add_parser("needed",
            help=_("show libraries (.so) required by matched packages"))
        needed_parser.add_argument("--quiet", "-q", action="store_true",
            default=self._quiet,
            help=_('quiet output, for scripting purposes'))
        needed_parser.add_argument("inrepo", action="store_const",
                                   const=None)
        needed_parser.add_argument("packages", nargs='+',
                                 metavar="<package>",
                                 help=_("package names"))
        needed_parser.set_defaults(func=self._needed)

        required_parser = subparsers.add_parser(
            "required",
            help=_("show packages requiring the given library name"))
        required_parser.add_argument(
            "libraries", nargs='+', metavar="<library>",
            help=_("library name (example: libdl.so.2)"))
        required_parser.add_argument("--quiet", "-q", action="store_true",
            default=self._quiet,
            help=_('quiet output, for scripting purposes'))
        required_parser.add_argument("inrepo", action="store_const",
                                     const=None)
        required_parser.set_defaults(func=self._required)

        revdeps_parser = subparsers.add_parser("revdeps",
            help=_("show reverse dependencies of packages"))
        revdeps_parser.add_argument("--quiet", "-q", action="store_true",
            default=self._quiet,
            help=_('quiet output, for scripting purposes'))
        revdeps_parser.add_argument("--verbose", "-v", action="store_true",
            default=self._verbose,
            help=_('verbose output, show more info'))
        revdeps_parser.add_argument("--bdeps", "-b", action="store_true",
            default=False,
            help=_('include build dependencies'))
        revdeps_parser.add_argument("inrepo", action="store_const",
                                    const=None)
        revdeps_parser.add_argument("packages", nargs='+',
                                 metavar="<package>",
                                 help=_("package names"))
        revdeps_parser.set_defaults(func=self._revdeps)

        sets_parser = subparsers.add_parser("sets",
            help=_("search through package sets"))
        sets_parser.add_argument("--quiet", "-q", action="store_true",
            default=self._quiet,
            help=_('quiet output, for scripting purposes'))
        sets_parser.add_argument("--verbose", "-v", action="store_true",
            default=self._verbose,
            help=_('verbose output, show package sets content'))
        sets_parser.add_argument("--in", metavar="<repository>",
            help=_("query into given repository only"),
            dest="inrepo", default=None)
        sets_parser.add_argument("sets", nargs='*',
                                 metavar="<set>",
                                 help=_("package set name"))
        sets_parser.set_defaults(func=self._sets)

        desc_parser = subparsers.add_parser("desc",
            help=_("search packages through their description"))
        desc_parser.add_argument("--quiet", "-q", action="store_true",
            default=self._quiet,
            help=_('quiet output, for scripting purposes'))
        desc_parser.add_argument("--verbose", "-v", action="store_true",
            default=self._verbose,
            help=_('verbose output, show more information'))
        desc_parser.add_argument("--in", metavar="<repository>",
            help=_("query into given repository only"),
            dest="inrepo", default=None)
        desc_parser.add_argument("descriptions", nargs='+',
                                 metavar="<description>",
                                 help=_("package description"))
        desc_parser.set_defaults(func=self._desc)

        return parser

    INTRODUCTION = """\
Toolset containing all the Entropy Server built-in repository query
tools.
"""

    def man(self):
        """
        Overridden from EitCommand.
        """
        return self._man()

    def parse(self):
        """ Overridden from EitCommand """
        parser = self._get_parser()
        try:
            nsargs = parser.parse_args(self._args)
        except IOError as err:
            sys.stderr.write("%s\n" % (err,))
            return parser.print_help, []

        # Python 3.3 bug #16308
        if not hasattr(nsargs, "func"):
            return parser.print_help, []

        self._repository_id = nsargs.inrepo
        self._quiet = nsargs.quiet
        self._verbose = getattr(nsargs, "verbose", self._verbose)
        self._nsargs = nsargs
        return self._call_shared, [nsargs.func, self._repository_id]

    def _tags(self, entropy_server):
        """
        Eit query tags code.
        """
        repository_ids = []
        if self._repository_id is None:
            repository_ids += entropy_server.repositories()
        else:
            repository_ids.append(self._repository_id)

        exit_st = 0
        for repository_id in repository_ids:
            repo = entropy_server.open_repository(repository_id)
            key_sorter = lambda x: repo.retrieveAtom(x[1])
            for tag in self._nsargs.tags:
                tagged_pkgs = repo.searchTaggedPackages(
                    tag, atoms = True)
                results = sorted(tagged_pkgs, key = key_sorter)
                for atom, pkg_id in results:
                    if self._quiet:
                        entropy_server.output(atom,
                            level="generic")
                    else:
                        print_package_info(pkg_id, entropy_server,
                                     repo, quiet = False)

                if (not results) and (not self._quiet):
                    entropy_server.output(
                        "%s: %s" % (
                            purple(_("Nothing found for")),
                            teal(tag)
                            ),
                        importance=1, level="warning")
                if not results:
                    exit_st = 1

        return exit_st

    def _needed(self, entropy_server):
        """
        Eit query needed code.
        """
        repository_ids = []
        if self._repository_id is None:
            repository_ids += entropy_server.repositories()
        else:
            repository_ids.append(self._repository_id)

        exit_st = 0
        for package in self._nsargs.packages:
            pkg_id, repo_id = entropy_server.atom_match(package)
            if pkg_id == -1:
                if not self._quiet:
                    entropy_server.output(
                        "%s: %s" % (
                            purple(_("Not matched")), teal(package)),
                        level="warning", importance=1)
                exit_st = 1
                continue
            repo = entropy_server.open_repository(repo_id)

            atom = repo.retrieveAtom(pkg_id)
            neededs = repo.retrieveNeeded(pkg_id)
            for needed in neededs:
                if self._quiet:
                    entropy_server.output(needed, level="generic")
                else:
                    entropy_server.output(needed)

            if not self._quiet:
                entropy_server.output(
                    "[%s] %s: %s %s" % (
                        purple(repo_id),
                        darkgreen(atom),
                        bold(str(len(neededs))),
                        teal(_("libraries found"))))

        return exit_st

    def _required(self, entropy_server):
        """
        Eit query required code.
        """
        repository_ids = []
        if self._repository_id is None:
            repository_ids += entropy_server.repositories()
        else:
            repository_ids.append(self._repository_id)

        exit_st = 0
        key_sorter = lambda x: entropy_server.open_repository(
            x[1]).retrieveAtom(x[0])
        for library in self._nsargs.libraries:

            pkg_matches = set()
            for repository_id in repository_ids:
                repo = entropy_server.open_repository(repository_id)
                package_ids = repo.searchNeeded(library, like=True)
                pkg_matches.update(
                    [(x, repository_id) for x in package_ids])

            matches = sorted(pkg_matches, key=key_sorter)
            for package_id, repository_id in matches:
                repo = entropy_server.open_repository(repository_id)
                if self._quiet:
                    atom = repo.retrieveAtom(package_id)
                    entropy_server.output(atom, level="generic")
                else:
                    print_package_info(
                        package_id, entropy_server,
                        repo, quiet=False)

            if not matches and not self._quiet:
                entropy_server.output(
                    "%s: %s" % (
                        purple(_("Nothing found for")),
                        teal(library)
                    ),
                    importance=1,
                    level="warning")
            if not matches:
                exit_st = 1

        return exit_st

    def _revdeps(self, entropy_server):
        """
        Eit query revdeps code.
        """
        excluded_dep_types = None
        if not self._nsargs.bdeps:
            excluded_dep_types = [
                etpConst['dependency_type_ids']['bdepend_id']
                ]

        exit_st = 0
        for package in self._nsargs.packages:
            package_id, repository_id = entropy_server.atom_match(package)
            if package_id == -1:
                if not self._quiet:
                    entropy_server.output(
                        "%s: %s" % (
                            purple(_("Not matched")), teal(package)),
                        level="warning", importance=1)
                exit_st = 1
                continue
            repo = entropy_server.open_repository(repository_id)

            key_sorter = lambda x: repo.retrieveAtom(x)
            results = repo.retrieveReverseDependencies(package_id,
                exclude_deptypes = excluded_dep_types)
            for pkg_id in sorted(results, key = key_sorter):
                print_package_info(pkg_id, entropy_server, repo,
                    installed_search = True, strict_output = self._quiet,
                    extended = self._verbose, quiet = self._quiet)

            if not self._quiet:
                atom = repo.retrieveAtom(package_id)
                entropy_server.output(
                    "[%s] %s: %s %s" % (
                        purple(repository_id),
                        darkgreen(atom),
                        bold(str(len(results))),
                        teal(_("revdep(s) found"))))

        return exit_st

    def _sets(self, entropy_server):
        """
        Eit query sets code.
        """
        repository_ids = []
        if self._repository_id is None:
            repository_ids += entropy_server.repositories()
        else:
            repository_ids.append(self._repository_id)
        repository_ids = tuple(repository_ids)
        sets = entropy_server.Sets()

        match_num = 0
        if not self._nsargs.sets:
            self._nsargs.sets.append("*")
        for item in self._nsargs.sets:
            results = sets.search(item, match_repo=repository_ids)
            key_sorter = lambda x: x[1]
            for repo, set_name, set_data in sorted(results,
                                                   key=key_sorter):
                match_num += 1
                found = True
                if not self._quiet:
                    entropy_server.output(
                        "%s%s" % (brown(etpConst['packagesetprefix']),
                                  darkgreen(set_name),))
                    if self._verbose:
                        elements = sorted(set_data)
                        for element in elements:
                            entropy_server.output(
                                teal(element),
                                header="  ")
                else:
                    entropy_server.output(
                        "%s%s" % (etpConst['packagesetprefix'],
                                  set_name,), level="generic")
                    if self._verbose:
                        for element in sorted(set_data):
                            entropy_server.output(
                                element, level="generic")

            if not self._quiet:
                entropy_server.output(
                    "[%s] %s %s" % (
                        darkgreen(item),
                        bold(str(match_num)),
                        teal(_("sets found"))))

        return 0

    def _desc(self, entropy_server):
        """
        Eit query desc code.
        """
        repository_ids = []
        if self._repository_id is None:
            repository_ids += entropy_server.repositories()
        else:
            repository_ids.append(self._repository_id)

        for repository_id in repository_ids:
            repo = entropy_server.open_repository(repository_id)
            key_sorter = lambda x: repo.retrieveAtom(x)
            for desc in self._nsargs.descriptions:
                pkg_ids = repo.searchDescription(desc, just_id = True)
                for pkg_id in sorted(pkg_ids, key = key_sorter):
                    if self._quiet:
                        entropy_server.output(
                            repo.retrieveAtom(pkg_id), level="generic")
                    else:
                        print_package_info(pkg_id, entropy_server, repo,
                                     extended = self._verbose,
                                     strict_output = False,
                                     quiet = False)

                if not self._quiet:
                    entropy_server.output(
                        "[%s] %s %s" % (
                            darkgreen(desc),
                            bold(str(len(pkg_ids))),
                            teal(_("packages found"))))

        return 0


EitCommandDescriptor.register(
    EitCommandDescriptor(
        EitQuery,
        EitQuery.NAME,
        _('miscellaneous package metadata queries'))
    )
