#!/usr/bin/python3
# Copyright (C) 2013-2016 Petter Reinholdtsen <pere@hungry.com>
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

__author__ = "Petter Reinholdtsen <pere@hungry.com>"

import sys
import gc
import gi
import fnmatch
import os
import urllib.request, urllib.error, urllib.parse
import errno

pkgdir = "/usr/share/isenkram"


def my_modaliases():
    modaliases = set()
    for dirpath, _, filenames in os.walk("/sys/devices"):
        if "modalias" in filenames:
            with open(dirpath + "/modalias") as sysfile:
                modaliases.add(sysfile.read().strip())
    with open("/proc/modules") as procfile:
        line = procfile.readline()
        while line:
            modalias = "lkmodule:%s" % (line.split(" "))[0]
            modaliases.add(modalias)
            line = procfile.readline()
        procfile.close()
    return list(modaliases)


def modalias_match(alias, candidates):
#    print "modalias_match('%s', %s)" % (alias, candidates)
    for part in candidates.split(')'):
        part = part.strip(', ')
        if not part:
            continue
        module, lst = part.split('(')
        for candidate in lst.split(','):
            candidate = candidate.strip()
            bus = candidate.split(':', 1)[0]
#            print bus, alias, candidate
#            print candidate, alias
            if fnmatch.fnmatch(alias, candidate):
#                print "Match"
                return True
    return False


def pkgs_handling_appstream_modaliases(modaliaslist):
    thepkgs = {}
    try:
        gi.require_version('AppStream', '1.0')
        from gi.repository import AppStream
    except ValueError:
        return []

    pool = AppStream.Pool()
    pool.load()
    try:
        cpts = pool.get_components().as_array()
    except AttributeError:
        # Handle old API too (before version 1.0)
        cpts = pool.get_components()
    ma_cpts = list()
    for cpt in cpts:
        provided = cpt.get_provided_for_kind(AppStream.ProvidedKind.MODALIAS)
        if provided:
            ma_cpts.append(cpt)

    for cpt in ma_cpts:
        prov = cpt.get_provided_for_kind(AppStream.ProvidedKind.MODALIAS)
        packages = cpt.get_pkgnames()
        for modalias in modaliaslist:
            for item in prov.get_items():
                pkgmodalias = str(item)
                # Transport modalias to module(alias) format to be
                # able to use modalias_match().  Perhaps better to
                # create a new modalias_match() like function without
                # that formatting requirement.
                if modalias_match(modalias, 'dummy(' + pkgmodalias + ')'):
                    for pkg in packages:
                        # blacklist entry due to too broad matching
                        # rule implemented to fix #838735 for
                        # broadcom-sta-dkms, to avoid proposing this
                        # package on everything with an ethernet card.
                        if pkg == 'broadcom-sta-dkms' and \
                           pkgmodalias == 'pci:v*d*sv*sd*bc02sc80i*':
                            pass
                        else:
                            thepkgs[pkg] = True
    return list(thepkgs.keys())


def check_packages_file(pkghash, fileref, modaliaslist):
    line = fileref.readline()
    while line:
        # Skip lines starting with comment character
        if 0 == line.find('#'):
            line = fileref.readline()
            continue
        header, sep, packagename = line.strip().partition(": ")
        header, sep, modaliases = fileref.readline().strip().partition(": ")
        blank = fileref.readline()
        for modalias in modaliaslist:
#            print "Match ", packagename, " ", modalias, " ", modaliases
            if modalias_match(modalias, modaliases):
                pkghash[packagename] = True
        line = fileref.readline()
    fileref.close()


def pkgs_handling_extra_modaliases(modaliaslist):
    """
Look up package-hardware mappings from local file
"""
    thepkgs = {}
    try:
        try:
            f = open("modaliases")
            check_packages_file(thepkgs, f, modaliaslist)
        except IOError as e:
            f = open(pkgdir + "/modaliases")
            check_packages_file(thepkgs, f, modaliaslist)
        f.close()

        for dirpath, _, filenames in os.walk(pkgdir + "/modaliases.d"):
            for filename in filenames:
                with open(dirpath + "/modalias") as aliasfile:
                    check_packages_file(thepkgs, aliasfile, modaliaslist)

    except:  # Ignore errors
        pass
#    print thepkgs
    return list(thepkgs.keys())


def main():
    if 1 < len(sys.argv):
        hwaliases = sys.argv[1:]
    else:
        hwaliases = my_modaliases()

    print("Locating packages supporting this hardware (AppStream):")
    for pkg in sorted(pkgs_handling_appstream_modaliases(hwaliases)):
        print("  %s" % pkg)

    print("Locating packages supporting this hardware (local file):")
    for pkg in sorted(pkgs_handling_extra_modaliases(hwaliases)):
        print("  %s" % pkg)

if __name__ == '__main__':
    main()
