# Copyright (C) 2011 Canonical
#
# Authors:
#  Matthew McGowan
#
# 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; version 3.
#
# 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

import json

try:
    from urllib.parse import urlencode
    urlencode  # pyflakes
except ImportError:
    from urllib import urlencode

from gi.repository import GObject

from softwarecenter.backend.ubuntusso import UbuntuSSO
from softwarecenter.enums import AppActions
from softwarecenter.db import DebFileApplication
from softwarecenter.distro import get_current_arch, get_distro
from softwarecenter.i18n import get_language
from softwarecenter.ui.gtk3.dialogs import dependency_dialogs
from softwarecenter.backend.transactionswatcher import (
    TransactionFinishedResult,
)

_appmanager = None  # the global AppManager instance


def get_appmanager():
    """ get a existing appmanager instance (or None if none is created yet) """
    return _appmanager


class ApplicationManager(GObject.GObject):

    __gsignals__ = {
        "purchase-requested": (GObject.SignalFlags.RUN_LAST,
                               None,
                               (GObject.TYPE_PYOBJECT, str, str,)
                               ),
    }

    def __init__(self, db, backend, icons):
        GObject.GObject.__init__(self)
        self._globalise_instance()
        self.db = db
        self.backend = backend
        self.distro = get_distro()
        self.icons = icons
        # its ok to use the sync version here to get the token, this is
        # very quick
        helper = UbuntuSSO()
        self.oauth_token = helper.find_oauth_token_sync()

    def _globalise_instance(self):
        global _appmanager
        if _appmanager is not None:
            msg = "Only one instance of ApplicationManager is allowed!"
            raise ValueError(msg)
        else:
            _appmanager = self

    def destroy(self):
        """Destroy the global instance."""
        global _appmanager
        _appmanager = None

    def request_action(self, app, addons_install, addons_remove, action):
        """callback when an app action is requested from the appview,
           if action is "remove", must check if other dependencies have to be
           removed as well and show a dialog in that case
        """
        #~ LOG.debug("on_application_action_requested: '%s' %s"
            #~ % (app, action))
        appdetails = app.get_details(self.db)
        if action == AppActions.REMOVE:
            if not dependency_dialogs.confirm_remove(
                        None, app, self.db, self.icons):
                    # craft an instance of TransactionFinishedResult to send
                    # with the transaction-stopped signal
                    result = TransactionFinishedResult(None, False)
                    result.pkgname = app.pkgname
                    self.backend.emit("transaction-stopped", result)
                    return
        elif action == AppActions.INSTALL:
            # If we are installing a package, check for dependencies that will
            # also be removed and show a dialog for confirmation
            # generic removal text (fixing LP bug #554319)
            if not dependency_dialogs.confirm_install(
                        None, app, self.db, self.icons):
                    # craft an instance of TransactionFinishedResult to send
                    # with the transaction-stopped signal
                    result = TransactionFinishedResult(None, False)
                    result.pkgname = app.pkgname
                    self.backend.emit("transaction-stopped", result)
                    return

        # this allows us to 'upgrade' deb files
        if (action == AppActions.UPGRADE and app.request and
                isinstance(app, DebFileApplication)):
            action = AppActions.INSTALL

        # action_func is one of:
        #     "install", "remove", "upgrade", "apply_changes"
        action_func = getattr(self.backend, action)
        if action == AppActions.INSTALL:
            # the package.deb path name is in the request
            if app.request and isinstance(app, DebFileApplication):
                debfile_name = app.request
            else:
                debfile_name = None

            action_func(app, appdetails.icon,
                        debfile_name, addons_install, addons_remove)
        elif callable(action_func):
            action_func(app, appdetails.icon,
                        addons_install=addons_install,
                        addons_remove=addons_remove)
        #~ else:
            #~ LOG.error("Not a valid action in AptdaemonBackend: '%s'" %
                #~ action)

    # public interface
    def reload(self):
        """ reload the package cache, this goes straight to the backend """
        self.backend.reload()

    def install(self, app, addons_to_install, addons_to_remove):
        """ install the current application, fire an action request """
        self.request_action(
            app, addons_to_install, addons_to_remove, AppActions.INSTALL)

    def remove(self, app, addons_to_install, addons_to_remove):
        """ remove the current application, , fire an action request """
        self.request_action(
            app, addons_to_install, addons_to_remove, AppActions.REMOVE)

    def upgrade(self, app, addons_to_install, addons_to_remove):
        """ upgrade the current application, fire an action request """
        self.request_action(
            app, addons_to_install, addons_to_remove, AppActions.UPGRADE)

    def apply_changes(self, app, addons_to_install, addons_to_remove):
        """ apply changes concerning add-ons """
        self.request_action(
            app, addons_to_install, addons_to_remove, AppActions.APPLY)

    def buy_app(self, app):
        """ initiate the purchase transaction """
        lang = get_language()
        appdetails = app.get_details(self.db)
        url = self.distro.PURCHASE_APP_URL % (
            lang, self.distro.get_codename(), urlencode({
                'archive_id': appdetails.ppaname,
                'arch': get_current_arch()
            })
        )
        self.emit("purchase-requested", app, appdetails.icon, url)

    def reinstall_purchased(self, app):
        """ reinstall a purchased app """
        #~ LOG.debug("reinstall_purchased %s" % self.app)
        appdetails = app.get_details(self.db)
        iconname = appdetails.icon
        deb_line = appdetails.deb_line
        license_key = appdetails.license_key
        license_key_path = appdetails.license_key_path
        signing_key_id = appdetails.signing_key_id
        oauth_token = json.dumps(self.oauth_token)
        self.backend.add_repo_add_key_and_install_app(deb_line,
                                                      signing_key_id,
                                                      app,
                                                      iconname,
                                                      license_key,
                                                      license_key_path,
                                                      oauth_token)

    def enable_software_source(self, app):
        """ enable the software source for the given app """
        appdetails = app.get_details(self.db)
        if appdetails.channelfile and appdetails._unavailable_channel():
            self.backend.enable_channel(appdetails.channelfile)
        elif appdetails.component:
            components = appdetails.component.split('&')
            for component in components:
                self.backend.enable_component(component)
