# Copyright 2013 Daniel Narvaez
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import subprocess

from osbuild import config
from osbuild import distro
from osbuild import state
from osbuild import utils
from osbuild import xvfb

_checkers = {}


def check(remove=False, update=False, test=False, interactive=True,
          lazy=True):
    if lazy:
        if state.system_check_is_unchanged():
            return True

    package_manager = \
        distro.get_package_manager(test=test, interactive=interactive)

    distro.print_distro_info()
    packages = config.load_packages()

    checks = config.load_prerequisites()
    if not _run_checks(package_manager, checks, packages):
        return False

    xvfb_proc, orig_display = xvfb.start()

    if not _run_checks(package_manager, config.load_dependencies(), packages):
        return False

    xvfb.stop(xvfb_proc, orig_display)

    print("All the required dependencies are installed.")

    if update:
        package_manager.update()

    if remove:
        _remove_packages(package_manager, packages)

    state.system_check_touch()

    return True


def _check_binary(check):
    return subprocess.call(["which", check],
                           stdout=utils.devnull,
                           stderr=subprocess.STDOUT)

_checkers["binary"] = _check_binary


def _check_pkgconfig(check):
    return subprocess.call(["pkg-config", "--exists", check]) == 1

_checkers["pkgconfig"] = _check_pkgconfig


def _check_python(check):
    return subprocess.call(["python", "-c", check],
                           stdout=utils.devnull,
                           stderr=subprocess.STDOUT) == 1

_checkers["python"] = _check_python


def _check_gtkmodule(check):
    # Not sure we can do better than this, the gtkmodule stuff is private
    missing = True

    for libdir in config.system_lib_dirs:
        if os.path.exists("%s/gtk-2.0/modules/lib%s.so" % (libdir, check)):
            missing = False

    return missing

_checkers["gtkmodule"] = _check_gtkmodule


def _check_include(check):
    return not os.path.exists(os.path.join("/usr/include/", check))

_checkers["include"] = _check_include


def _check_dbus(check):
    found = False

    for base_dir in "system-services", "services":
        if os.path.exists(os.path.join("/usr/share/dbus-1",
                                       base_dir, "%s.service" % check)):
            found = True

    return not found

_checkers["dbus"] = _check_dbus


def _check_metacity_theme(check):
    theme = "/usr/share/themes/%s/metacity-1/metacity-theme-3.xml"
    return not os.path.exists(theme % check)

_checkers["metacity-theme"] = _check_metacity_theme


def _check_gstreamer(check, version):
    missing = True

    for libdir in config.system_lib_dirs:
        if os.path.exists("%s/gstreamer-%s/libgst%s.so" %
                          (libdir, version, check)):
            missing = False

    return missing


def _check_gstreamer_0_10(check):
    return _check_gstreamer(check, "0.10")

_checkers["gstreamer-0.10"] = _check_gstreamer_0_10


def _check_gstreamer_1_0(check):
    return _check_gstreamer(check, "1.0")

_checkers["gstreamer-1.0"] = _check_gstreamer_1_0


def _print_checks(checks):
    for check in checks:
        print("[%s] %s" % (check["checker"], check["check"]))


def _eval_check_if(check):
    if "check_if" not in check:
        return True

    distro_info = distro.get_distro_info()
    globals = {"distro": "%s-%s" % (distro_info.name, distro_info.version)}

    print(eval(check["check_if"], globals))

    return eval(check["check_if"], globals) == "True"


def _run_checks(package_manager, checks, packages):
    distro_info = distro.get_distro_info()

    failed_checks = []
    packages_not_found = []
    to_install = []

    for check in checks:
        if not _eval_check_if(check):
            continue

        checker = _checkers[check["checker"]]
        if checker(check["check"]):
            try:
                packages_for_check = packages[check["name"]][distro_info.name]
            except KeyError:
                packages_for_check = []
                packages_not_found.append(check)

            for package in packages_for_check:
                if package not in to_install:
                    to_install.append(package)

            failed_checks.append(check)

    if distro_info.supported:
        if packages_not_found:
            print("\nPackages not found for")
            _print_checks(packages_not_found)
            return False

        if to_install:
            package_manager.install_packages(to_install)
    elif failed_checks:
        print("Failed checks\n")
        _print_checks(failed_checks)

        if to_install:
            print("\nYou might try to install the following packages\n")
            print(" ".join(to_install))

        return False

    return True


def _remove_packages(package_manager, packages):
    distro_name = distro.get_distro_info().name

    to_keep = []
    for package_info in packages.values():
        if distro_name in package_info:
            for package in package_info[distro_name]:
                if package not in to_keep:
                    to_keep.append(package)

    try:
        to_keep = package_manager.find_with_deps(to_keep)
    except NotImplementedError:
        return

    all = package_manager.find_all()

    to_remove = []
    for package in all:
        if package not in to_keep:
            to_remove.append(package)

    if to_remove:
        package_manager.remove_packages(to_remove)
