#!/usr/bin/python
# Licensed under the GNU General Public License Version 2
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# Copyright (C) 2008
#    Richard Hughes <richard@hughsie.com>

# imports
from .enums import *
from .package import PackagekitPackage
import collections

class PackagekitFilter(object, PackagekitPackage):

    def __init__(self, fltlist="none"):
        ''' save state '''
        self.fltlist = fltlist
        self.package_list = [] #we can't do emitting as found if we are post-processing
        self.installed_unique = {}

    def add_installed(self, pkgs):
        ''' add a list of packages that are already installed '''
        for pkg in pkgs:
            self.package_list.append((pkg, INFO_INSTALLED))

    def add_available(self, pkgs):
        ''' add a list of packages that are available '''
        for pkg in pkgs:
            self.package_list.append((pkg, INFO_AVAILABLE))

    def add_custom(self, pkg, info):
        ''' add a custom packages indervidually '''
        self.package_list.append((pkg, info))

    def _filter_base(self, pkg):
        ''' do extra filtering (gui, devel etc) '''
        for flt in self.fltlist:
            if flt in (FILTER_GUI, FILTER_NOT_GUI):
                if not self._do_gui_filtering(flt, pkg):
                    return False
            elif flt in (FILTER_DEVELOPMENT, FILTER_NOT_DEVELOPMENT):
                if not self._do_devel_filtering(flt, pkg):
                    return False
            elif flt in (FILTER_FREE, FILTER_NOT_FREE):
                if not self._do_free_filtering(flt, pkg):
                    return False
            elif flt in (FILTER_ARCH, FILTER_NOT_ARCH):
                if not self._do_arch_filtering(flt, pkg):
                    return False
        return True

    def _filter_installed(self, pkg):
        ''' do extra filtering (gui, devel etc) '''
        for flt in self.fltlist:
            if flt in (FILTER_INSTALLED, FILTER_NOT_INSTALLED):
                if not self._do_installed_filtering(flt, pkg):
                    return False
        return True

    def get_package_list(self):
        '''
        do filtering we couldn't do when generating the list
        '''

        # filter common things here like architecture
        # NOTE: we can't do installed and ~installed here as we need
        # this data for the newest and downgrade checks below
        package_list = self.package_list
        self.package_list = []
        for pkg, state in package_list:
            if self._filter_base(pkg):
                self.package_list.append((pkg, state))

        # prepare lookup table of installed packages
        installed_dict = collections.defaultdict(list)
        for pkg, state in self.package_list:
            if state is INFO_INSTALLED:
                installed_dict[self._pkg_get_name(pkg)].append(pkg)

        # check there are not available versions in the package list
        # that are older than the installed version
        package_list = self.package_list
        self.package_list = []
        for pkg, state in package_list:

            add = True
            if state is INFO_AVAILABLE:
                for pkg_tmp in installed_dict[self._pkg_get_name(pkg)]:
                    rc = self._pkg_compare(pkg, pkg_tmp)

                    # don't add if the same as the installed package
                    # or a downgrade to the existing installed package
                    if rc == 0 or rc == -1:
                        add = False
                        break

            if add:
                self.package_list.append((pkg, state))

        # filter installed state last
        package_list = self.package_list
        self.package_list = []
        for pkg, state in package_list:
            if self._filter_installed(pkg):
                self.package_list.append((pkg, state))

        # do the backend specific filtering
        return self.post_process()

    def post_process(self):
        '''
        do filtering we couldn't do when generating the list
        Needed to be implemented in a sub class
        '''
        return self.package_list

    def _pkg_compare(self, pkg1, pkg2):
        '''
        Returns a version comparison of the packages, where:
        -2 : pkg1 not comparable with pkg2
        -1 : pkg2 is newer than pkg1
         0 : pkg1 == pkg2
         1 : pkg1 is newer than pkg2
         2 : not implemented
        Needed to be implemented in a sub class
        '''
        return 2

    def _pkg_get_name(self, pkg):
        '''
        Returns the name of the package used for duplicate filtering
        Needed to be implemented in a sub class
        '''
        return None

    def _pkg_is_installed(self, pkg):
        '''
        Return if the package is installed.
        Needed to be implemented in a sub class
        '''
        return True

    def _pkg_is_devel(self, pkg):
        '''
        Return if the package is development.
        Needed to be implemented in a sub class
        '''
        return True

    def _pkg_is_gui(self, pkg):
        '''
        Return if the package is a GUI program.
        Needed to be implemented in a sub class
        '''
        return True

    def _pkg_is_free(self, pkg):
        '''
        Return if the package is free software.
        Needed to be implemented in a sub class
        '''
        return True

    def _pkg_is_arch(self, pkg):
        '''
        Return if the package is the same architecture as the machine.
        Needed to be implemented in a sub class
        '''
        return True

    def _do_installed_filtering(self, flt, pkg):
        is_installed = self._pkg_is_installed(pkg)
        if flt == FILTER_INSTALLED:
            want_installed = True
        else:
            want_installed = False
        return is_installed == want_installed

    def _do_devel_filtering(self, flt, pkg):
        is_devel = self._pkg_is_devel(pkg)
        if flt == FILTER_DEVELOPMENT:
            want_devel = True
        else:
            want_devel = False
        return is_devel == want_devel

    def _do_gui_filtering(self, flt, pkg):
        is_gui = self._pkg_is_gui(pkg)
        if flt == FILTER_GUI:
            want_gui = True
        else:
            want_gui = False
        return is_gui == want_gui

    def _do_free_filtering(self, flt, pkg):
        is_free = self._pkg_is_free(pkg)
        if flt == FILTER_FREE:
            want_free = True
        else:
            want_free = False
        return is_free == want_free

    def _do_arch_filtering(self, flt, pkg):
        is_arch = self._pkg_is_arch(pkg)
        if flt == FILTER_ARCH:
            want_arch = True
        else:
            want_arch = False
        return is_arch == want_arch

