#!/usr/bin/env python3
#  Copyright  2019 Alexis Lopez Zubieta
#
#  Permission is hereby granted, free of charge, to any person obtaining a
#  copy of this software and associated documentation files (the "Software"),
#  to deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
import os
import logging
import argparse

from AppImageBuilder.AppDir2 import AppDir2

from AppImageBuilder.tools import Linker

from AppImageBuilder.tools import Dpkg


class Node:
    path = ""
    children = []

    def __init__(self, path):
        self.path = path


class Graph:
    roots = {}
    nodes = {}

    def add(self, path, dependcies_paths):
        node = None
        if path in self.nodes:
            node = self.nodes[path]
        else:
            node = Node(path)

            self.roots[path] = node
            self.nodes[path] = node

        for dependency_path in dependcies_paths:
            if dependency_path in self.roots:
                del self.roots[dependency_path]

            child = None
            if dependency_path in self.nodes:
                child = self.nodes[dependency_path]
            else:
                child = Node(dependency_path)
                self.nodes[dependency_path] = child

            node.children.append(child)


def main():
    parser = argparse.ArgumentParser(description='AppImage crafting tool')
    parser.add_argument('--log', dest='loglevel', default="INFO",
                        help='logging level (default: INFO)')
    parser.add_argument('AppDir', type=str, help='target AppDir to inspect')
    parser.add_argument('--print-roots', dest='do_print_roots', action='store_true',
                        help='print the dependency graph roots')
    parser.add_argument('--print-roots-packages', dest='do_print_roots_packages', action='store_true',
                        help='print the dependency graph roots packages')

    args = parser.parse_args()
    configure_logger(args.loglevel)

    app_dir = AppDir2(args.AppDir)

    graph = build_dependencies_graph(app_dir)

    if args.do_print_roots:
        for key in graph.roots:
            print(key)

    if args.do_print_roots_packages:
        root_packages = set()
        dpkg = Dpkg()
        for path in graph.roots:
            original_path = path.replace(app_dir.path, '')
            logging.debug("Looking for file package : %s" % path)
            packages = dpkg.find_owner_packages(original_path)
            logging.debug("File package: %s" % ' '.join(packages))
            root_packages.update(packages)

        for pkg in root_packages:
            print(pkg)


def configure_logger(loglevel):
    numeric_level = getattr(logging, loglevel.upper())
    if not isinstance(numeric_level, int):
        logging.error('Invalid log level: %s' % loglevel)
    logging.basicConfig(level=numeric_level)


def build_dependencies_graph(app_dir):
    linker_path = Linker.find_binary_path(app_dir.path)
    linker = Linker(linker_path)

    linkable_files = linker.list_linkable_files(app_dir.path)

    lib_dirs = get_library_dirs(linkable_files)

    graph = Graph()

    for file in linkable_files:
        logging.debug("Inspecting dependencies of: %s" % file)
        if file not in graph.nodes:
            dependencies = linker.list_link_dependencies(file, ignore_cache=True, library_dirs=lib_dirs)
            if not dependencies:
                dependencies = set()
            else:
                dependencies = set(dependencies.values())

            dependencies.discard(None)
            logging.debug("%s" % " ".join(dependencies))
            graph.add(file, list(dependencies))
        else:
            logging.debug("Skipped")

    return graph


def get_library_dirs(linkable_files):
    lib_dirs = set()
    for file in linkable_files:
        lib_dirs.add(os.path.dirname(file))
    return lib_dirs


if __name__ == '__main__':
    main()
