# -*- coding: utf-8 -*-

# Copyright 2012-2016 Mir Calculate. http://www.calculate-linux.org
#
#  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.

from calculate.consolegui import qt
import time, urllib2
from calculate.core.client.function import create_obj

try:
    from calculate.update.update_info import UpdateInfo
except ImportError:
    UpdateInfo = None
import dbus

from ReturnMessage import ReturnedMessage
from calculate.consolegui.application.pid_information import client_pid_info, \
    client_list_pid
from more import show_msg, get_sid, _print, get_icon

# class MySignal(qt.QObject):
#    sig = qt.Signal(str, str)

class TrayUpdateSystem(qt.QThread):
    mes_sig = qt.Signal(str, str)
    started_sig = qt.Signal(str)

    def __init__(self, target, parent=None):
        qt.QThread.__init__(self, parent)
        self._parent = target

    def run(self):
        self.close_flag = False
        if not hasattr(self._parent, 'localhost_ClientObj'):
            self.mes_sig.emit(_('Connection Error'), \
                              _(
                                  'You are not connected to the localhost server'))
            return 1
        if not self._parent.localhost_ClientObj.client:
            self.mes_sig.emit(_('Connection Error'), \
                              _(
                                  'You are not connected to the localhost server'))
            return 1

        sid = get_sid(self._parent.localhost_ClientObj.client)

        try:
            param_object = create_obj(self._parent.localhost_ClientObj.client,
                                      "update")
            meth_result_temp = \
                self._parent.localhost_ClientObj.client.service.update(
                    sid, param_object)
        except Exception, e:
            msg = e.message
            if type(e.message) == tuple:
                msg = ' '.join(map(lambda x: str(x), list(e.message)))

            self.mes_sig.emit(msg, 'cl-client-console Exception')
            return 1
        meth_result = []
        method_name = 'System update tray'
        error = False
        for res in meth_result_temp[0]:
            meth_result.append(ReturnedMessage(res.type, res.field, \
                                               res.message, res.expert))
            if res.type == 'error':
                error = True

        if error:
            return 1
        if meth_result[0].type == "pid":
            # create process dict
            self._parent.localhost_ClientObj.process_dict \
                [meth_result[0].message] = {}
            self._parent.localhost_ClientObj.process_dict \
                [meth_result[0].message]['result'] = meth_result[0]
            self._parent.localhost_ClientObj.process_dict \
                [meth_result[0].message]['name'] = method_name
            self._parent.localhost_ClientObj.process_dict \
                [meth_result[0].message]['layout'] = qt.QVBoxLayout()
            self._parent.localhost_ClientObj.process_dict \
                [meth_result[0].message]['layout'].setAlignment \
                (qt.Qt.AlignTop)
        else:
            return 1

        # self.mes_sig.emit('', _('Update running'))

        mess = meth_result[0]
        pid = mess.message
        self._parent.sys_update_pid = int(pid)
        self.started_sig.emit(pid)

        # self.get_update_status(sid, pid)

    def get_update_status(self, sid, pid):
        end_frame = True

        try:
            current_frame = self._parent.localhost_ClientObj.client. \
                service.get_entire_frame(sid, pid)
        except urllib2.URLError:
            _print('get_entire_frame in TrayIcon Exception')
            current_frame = None
        while end_frame:
            while current_frame in [None, [], ""]:
                for i in range(5):
                    time.sleep(0.1)
                    if self.close_flag:
                        # self.mes_sig.emit('', _('Update aborted'))
                        return 0
                try:
                    client = self._parent.localhost_ClientObj.client
                    current_frame = client.service.get_frame(sid, pid, "gui")
                except urllib2.URLError:
                    _print('client.service.get_entire_frame in'
                           ' TrayIcon Exception')
                    current_frame = None
            for item in range(len(current_frame[0])):
                if current_frame[0][item].type == 'error':
                    # self.mes_sig.emit(_("Failed to update!"), \
                    #                    current_frame[0][item].message)
                    end_frame = False
                    return 1
                elif current_frame[0][item].type == 'endFrame':
                    if current_frame[0][item].message:
                        message = current_frame[0][item].message
                    else:
                        message = ''
                    # self.mes_sig.emit(_('Update successfully completed'), \
                    #                        message)
                    end_frame = False
            current_frame = None

    def close(self):
        self.close_flag = True


class TrayIcon(qt.QSystemTrayIcon):
    default_theme_name = 'Tango'

    def __init__(self, parent):
        super(TrayIcon, self).__init__(parent)
        self._parent = parent
        self.actions = []

        self.setIcon(get_icon(
            '/usr/share/pixmaps/calculate-console-offline.svg'))

        self.is_online_icon = False

        # для решения странного размера иконки в mate
        self.hide()
        self.show()
        self.generation_actions()
        self.right_menu = self.create_menu('help', 'bug_report', None, 'tools',
                                           'update_system', None, 'exit')

        self.activated.connect(self.activ)
        self.setToolTip(_('Calculate Console'))
        self.setContextMenu(self.right_menu)

        self.update_thread = TrayUpdateSystem(parent)
        self.update_thread.mes_sig.connect(self.showMessage)
        self.update_thread.started_sig.connect(self.showUpdate)

    def generation_actions(self):
        _("About")
        _("Report a Bug")
        _("Program settings")
        _("Update System")
        _("Exit Program")
        self.actions = {
            'help': {
                'icons': ['help-about', 'help-browser'],
                'label': "About",
                'trigger': self.help},
            'bug_report': {
                'icons': ['tools-report-bug', 'system-help',
                          'help-browser'],
                'label': "Report a Bug",
                'trigger': self.bug_report},
            'tools': {
                'icons': ['preferences-other', 'preferences-system'],
                'label': "Program settings",
                'trigger': self.tools},
            'update_system': {
                'icons': ['system-software-update'],
                'label': "Update System",
                'trigger': self.start_update_system},
            'exit': {
                'icons': ["application-exit", "system-log-out"],
                'label': "Exit Program",
                'trigger': self.exit},
            'show_hide': {
                'icons': ['preferences-system-windows'],
                'label': "Show/Hide Window",
                'trigger': self.windowVisible}
        }
        for action_name, action_data in self.actions.items():
            setattr(self, '%s_action' % action_name,
                    qt.QAction(get_icon(*action_data['icons']),
                               _(action_data['label']), self,
                               triggered=action_data['trigger']))

    def create_menu(self, *actions):
        menu = qt.QMenu()
        for action in actions:
            if action:
                menu.addAction(getattr(self, "%s_action" % action))
            else:
                menu.addSeparator()
        return menu

    def check_for_update(self):
        if hasattr(self._parent, "localhost_ClientObj"):
            if self._parent.localhost_ClientObj:
                if UpdateInfo:
                    uinfo = UpdateInfo(self._parent.localhost_ClientObj.VarsApi)
                    return (uinfo.need_update() and
                            not uinfo.update_already_run() and
                            not self.get_update_pid() and
                            not self.update_thread.isRunning())
        return False

    def set_icon(self, online, has_update=None):
        if online:
            if not self.is_online_icon:
                self.setIcon(get_icon(
                    '/usr/share/pixmaps/calculate-console-online.svg'))
                # для решения странного размера иконки в mate
                self.hide()
                self.show()
                self.is_online_icon = True
        else:
            if self.is_online_icon:
                self.setIcon(get_icon(
                    '/usr/share/pixmaps/calculate-console-offline.svg'))
                # для решения странного размера иконки в mate
                self.hide()
                self.show()
                self.is_online_icon = False

    def translate(self):
        for action_name, action_data in self.actions.items():
            getattr(self, '%s_action' % action_name).setText(
                _(action_data['label']))

    def help(self):
        self._parent.currentWidget().help()

    def bug_report(self):
        self._parent.currentWidget().bug_report()

    def tools(self):
        self._parent.currentWidget().tools()

    def get_update_pid(self):
        """
        Получить pid процесса выполняющего update для этой сессии
        """
        pid = str(self._parent.sys_update_pid)
        client_obj = self._parent.localhost_ClientObj
        pids = map(str, client_list_pid(client_obj.client))
        client_pid_info(client_obj, client_obj.client, 0)
        if self._parent.localhost_ClientObj:
            if pid and pid in client_obj.process_dict and pid in pids:
                return pid
            else:
                for pid in pids:
                    d = client_obj.process_dict[pid]
                    if (d['method_name'] == 'update'
                        and d.get('status', '0') == '1'):
                        return pid

    def show_update(self):
        pid = self.get_update_pid()
        if pid:
            client_obj = self._parent.localhost_ClientObj
            client_pid_info(client_obj, client_obj.client, 0)

            client_obj._parent.setWindowTitle \
                ("Calculate console" + ' - ' + _('System Update'))
            result = client_obj.process_dict[pid]['result']
            client_obj.MainWidget.main_view_process('update', result, pid)
            client_obj.app.processEvents()
            vsb = client_obj.MainWidget.main_frame.verticalScrollBar()
            vsb.setValue(vsb.maximum())
            self._parent.hide()
            self._parent.show()
            self._parent.showNormal()
        else:
            self._parent.show()
            self._parent.move(self._parent.cur_pos)
            self._parent.show()
        return True

    def exit(self):
        # if self.update_thread.isRunning():
        #    text = _('The system is updated')
        #    informative_text = _('Stop updating and exit?')
        #    reply = show_question(self._parent, text, informative_text)

        #    if reply == qt.QMessageBox.No:
        #        return 0

        #    elif reply == qt.QMessageBox.Yes:
        #        self.update_thread.close()
        #        self.update_thread.wait()
        self._parent._closeEvent()

    def activ(self, reason):
        if reason == qt.QSystemTrayIcon.Trigger:
            self.windowVisible()
        if reason == qt.QSystemTrayIcon.DoubleClick:
            self.windowVisible()

    def showMessage(self, title, message, icon='dialog-warning'):

        # This uses the session bus because it's session-specific.
        bus = dbus.SessionBus()
        proxy = bus.get_object('org.freedesktop.Notifications', \
                               '/org/freedesktop/Notifications')
        interface = dbus.Interface(proxy, dbus_interface= \
            'org.freedesktop.Notifications')

        interface.Notify('cl-console-gui', 1, icon, title, message, [], {}, -1)

        # KDE KNotify
        # bus = dbus.SessionBus()
        # knotify = bus.get_object('org.kde.knotify', '/Notify')

        # knotify.event("warning", "kde", [], 'title', 'text', [], [], 0, 0,
        #              dbus_interface="org.kde.KNotify")

    def windowVisible(self):
        if self._parent.isVisible():
            self._parent.cur_pos = self._parent.pos()
            self._parent.hide()
        else:
            self._parent.show()
            self._parent.move(self._parent.cur_pos)

    def start_update_system(self):
        self.set_icon(True, False)
        if self.get_update_pid():
            self.show_update()
        else:
            self.update_thread.start()
            # if not self.update_thread.isRunning():

    def showUpdate(self, message):
        self.show_update()
