#!/usr/bin/python2 -tt
# -*- coding:  utf-8 -*-

#    Fedora Gooey Karma prototype
#    based on the https://github.com/mkrizek/fedora-gooey-karma
#
#    Copyright (C) 2013
#
#    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 3 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, see <http://www.gnu.org/licenses/>.
#
#    Author: Branislav Blaskovic <branislav@blaskovic.sk>
#    Author: Tomas Meszaros <exo@tty.sk>

import os
import re
import sys

sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), '../share/fedora-gooey-karma'))

import Queue
import rpm
import datetime
import koji
from PySide import QtCore
from PySide import QtGui

from customgui import CustomTreeWidgetItem
from mainwindow_gui import Ui_MainWindow
from packagesworker import PackagesWorker
from bodhiworker import BodhiWorker
from config import Config
from toolbox import Toolbox
from browser import WebBrowser

from idlequeuedispatcher import ThreadDispatcher
from sendkarma import SendKarma

class MainWindow(QtGui.QMainWindow):

    __FEDORA_RELEASES = ['Fedora 19', 'Fedora 18', 'Fedora 17']
    __BODHI_WORKERS_COUNT = 15

    def __init__(self, parent=None):
        # GUI
        super(MainWindow, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

        # Holders of data
        self.__installed_packages = {}
        self.installed_updates = {}
        self.available_updates = {}
        self.__process_list = []

        # Config
        self.config = Config()
        ## Set fas username and password to widget
        self.ui.usernameEdit.setText(self.config.get_fas_name())
        self.ui.passwordEdit.setText(self.config.get_fas_password())
        ## Set side tabbar index
        self.ui.tabWidget_side.setCurrentIndex(int(self.config.tab_side_index))

        # Toolbox/WebBrowser classes
        self.toolbox = Toolbox(self)
        self.webbrowser = WebBrowser(self)

        # Prepare Queues
        self.bodhi_workers_queue = Queue.Queue()
        self.pkg_worker_queue = Queue.Queue()

        # Prepare ui
        self.rpmTS = rpm.TransactionSet()
        self.__load_and_set_fedora_releases()
        self.ui.tabWidget.setCurrentIndex(0)
        self.ui.toolBoxWhatToTest.setCurrentIndex(0)
        self.ui.karmaFilterWidget.setVisible(False)
        self.toolbox.update_favorite_ignored_pkg_lists()

        # Progress bar
        self.progressBar = QtGui.QProgressBar()
        self.progressBar.setMinimum(0)
        self.ui.centralWidgetSplitter.addWidget(self.progressBar)
        self.progressBar.hide()

        # TabBar - remove package's tabs and save it for later use
        self.__tabBar_info_backup = None
        self.__remove_package_tabs()

        # Thread Dispatcher to run methods thread-safely
        self.dispatcher = ThreadDispatcher(self)
        self.dispatcher.start()

        # Connects
        ## Top main menu
        self.ui.actionQuit.triggered.connect(QtCore.QCoreApplication.instance().quit, QtCore.Qt.QueuedConnection)
        self.ui.actionAbout.triggered.connect(self.__show_message_box_about, QtCore.Qt.QueuedConnection)
        ## Misc connects
        self.ui.pkgList.currentItemChanged.connect(self.__show_package_detail, QtCore.Qt.QueuedConnection)
        self.ui.searchEdit.textChanged.connect(self.__populate_pkgList, QtCore.Qt.QueuedConnection)
        self.ui.installedBtn.clicked.connect(self.__show_installed, QtCore.Qt.QueuedConnection)
        self.ui.availableBtn.clicked.connect(self.__show_available, QtCore.Qt.QueuedConnection)
        self.ui.loadPackagesBtn.clicked.connect(self.__start_pkg_worker, QtCore.Qt.QueuedConnection)
        self.ui.treeWidget_bugs.itemDoubleClicked.connect(self.webbrowser.show_bug_in_browser, QtCore.Qt.QueuedConnection)
        self.ui.treeWidget_related_packages.itemDoubleClicked.connect(self.webbrowser.show_relevant_pkg_in_browser, QtCore.Qt.QueuedConnection)
        self.ui.treeWidget_test_cases.itemDoubleClicked.connect(self.webbrowser.show_testcase_in_browser, QtCore.Qt.QueuedConnection)
        self.ui.karmaCheckBox.stateChanged.connect(self.__populate_pkgList, QtCore.Qt.QueuedConnection)
        self.ui.karmaFilterUserCombo.currentIndexChanged.connect(self.__populate_pkgList, QtCore.Qt.QueuedConnection)
        self.ui.karmaUsernameEdit.textChanged.connect(self.__populate_pkgList, QtCore.Qt.QueuedConnection)
        self.ui.sendBtn.clicked.connect(self.__send_comment, QtCore.Qt.QueuedConnection)
        self.ui.tabWidget_side.currentChanged.connect(self.__tabside_changed)
        ## Package settings
        self.ui.pkg_settings_open_bodhi_update.clicked.connect(self.webbrowser.show_bodhi_update_in_browser, QtCore.Qt.QueuedConnection)
        self.ui.pkg_settings_download_source_rpm.clicked.connect(self.webbrowser.download_source_rpm, QtCore.Qt.QueuedConnection)
        self.ui.settings_pkg_cat_normal.toggled.connect(self.__set_pkg_category, QtCore.Qt.QueuedConnection)
        self.ui.settings_pkg_cat_favorite.toggled.connect(self.__set_pkg_category, QtCore.Qt.QueuedConnection)
        self.ui.settings_pkg_cat_ignored.toggled.connect(self.__set_pkg_category, QtCore.Qt.QueuedConnection)
        ## What to test toolbox
        self.ui.tool_pkg_list_negative.itemClicked.connect(self.toolbox.negative_karma_clicked, QtCore.Qt.QueuedConnection)
        self.ui.tool_pkg_list_favorite.itemClicked.connect(self.toolbox.favorite_item_clicked, QtCore.Qt.QueuedConnection)
        self.ui.tool_pkg_list_running.itemClicked.connect(self.toolbox.currently_running_clicked, QtCore.Qt.QueuedConnection)
        self.ui.toolBoxWhatToTest.currentChanged.connect(self.toolbox.move_toolbox_buttons, QtCore.Qt.QueuedConnection)
        self.ui.btn_tool_add_pkg.clicked.connect(self.toolbox.config_add_package, QtCore.Qt.QueuedConnection)
        self.ui.btn_tool_remove_pkg.clicked.connect(self.toolbox.config_remove_package, QtCore.Qt.QueuedConnection)

        # Status list mouse click expand
        def increaseSize(event):
            self.ui.statusList.setMaximumHeight(400)

        def decreaseSize(event):
            self.ui.statusList.setMaximumHeight(80)

        self.ui.statusList.mousePressEvent = increaseSize
        self.ui.statusList.mouseReleaseEvent = decreaseSize

        # Start Bodhi workers
        self.bodhi_workers = []
        for i in range(self.__BODHI_WORKERS_COUNT):
            b = BodhiWorker("bodhi_worker" + str(i), self.bodhi_workers_queue, self)
            b.start()
            self.bodhi_workers.append(b)

        # Start package worker
        self.pkg_worker = PackagesWorker(self.pkg_worker_queue, self.bodhi_workers_queue, self.__BODHI_WORKERS_COUNT, self)
        self.pkg_worker.start()

    def customEvent(self, event):
        # Process idlequeue_dispatcher events
        event.callback()

    def __tabside_changed(self, index):
        self.config.tab_side_index = index
        self.config.save_config()

    def __set_pkg_category(self, checked):
        # There are 2 actions on every click. We just need once
        if not checked:
            return
        # When radio button in settings is toggled, assign package to one
        # of the categories in config
        update = self.get_bodhi_update()
        pkg_name = update['parsed_nvr']['name']

        if self.ui.settings_pkg_cat_normal.isChecked():
            self.config.favorited_packages.remove_package(pkg_name)
            self.config.ignored_packages.remove_package(pkg_name)
        elif self.ui.settings_pkg_cat_favorite.isChecked():
            self.config.favorited_packages.add_package(pkg_name)
            self.config.ignored_packages.remove_package(pkg_name)
        elif self.ui.settings_pkg_cat_ignored.isChecked():
            self.config.favorited_packages.remove_package(pkg_name)
            self.config.ignored_packages.add_package(pkg_name)

        self.config.save_config()
        self.toolbox.update_favorite_ignored_pkg_lists()

    def set_num_of_pkgs_to_process(self, num):
        # Set maximum size of progressbar according to num of packages to process in Bodhi
        self.progressBar.setMaximum(int(num))
        self.progressBar.show()

    def __remove_package_tabs(self):
        # This will backup tab from layout
        if self.__tabBar_info_backup is None:
            self.__tabBar_info_backup = self.ui.tabWidget.widget(1)
        self.ui.tabWidget.removeTab(1)

    def __show_message_box_about(self):
        text = (
            "<b>Fedora Gooey Karma</b> is a tool for browsing installed packages from updates-testing repository.<br><br>"
            "You can easily add karma comment, open bugs attached to new build and more.<br><br>"
            "Please feel free to <a href=\"https://github.com/blaskovic/fedora-gooey-karma/issues\">report any issues</a> and "
            "visit <a href=\"https://github.com/blaskovic/fedora-gooey-karma\">homepage</a> of Fedora Gooey Karma.<br><br>"
            "<b>Authors:</b><br>"
            "<ul><li>Branislav Blaskovic</li><li>Tomas Meszaros</li></ul>"
        )
        QtGui.QMessageBox.about(QtGui.QApplication.activeWindow(), "Fedora Gooey Karma", text)

    def add_status_item(self, item):
        self.ui.statusList.addItem(str(item))
        self.ui.statusList.scrollToBottom()

    def bodhi_process_result(self, result):
        variant, data = result

        # We've got one bodhi update of installed package, add it to pkg list
        if variant == 'installed':
            self.installed_updates[data['itemlist_name']] = data
            self.add_status_item(data['itemlist_name'] + " is installed")

            if self.ui.installedBtn.isChecked():
                # Is this package in list?
                # If yes, remove it to add it again
                for i in range(self.ui.pkgList.count()):
                    item = self.ui.pkgList.item(i)
                    if item.text() == data['itemlist_name']:
                        self.ui.pkgList.takeItem(i)
                        self.ui.pkgList.removeItemWidget(item)
                        break

                # Add update to list if it match filters
                if self.__update_can_be_shown(data):
                    self.ui.pkgList.addItem(data['itemlist_name'])

        # We've got available package update, save it
        elif variant == 'available':
            self.available_updates[data['itemlist_name']] = data
            self.add_status_item(data['itemlist_name'] + " can be updated")

            if self.ui.availableBtn.isChecked():
                # Is this package in list?
                # If yes, remove it to add it again
                for i in range(self.ui.pkgList.count()):
                    item = self.ui.pkgList.item(i)
                    if item.text() == data['itemlist_name']:
                        self.ui.pkgList.takeItem(i)
                        self.ui.pkgList.removeItemWidget(item)
                        break

                # Add update to list if it match filters
                if self.__update_can_be_shown(data):
                    self.ui.pkgList.addItem(data['itemlist_name'])

        # Prepare What to test section
        if variant in ['installed', 'available']:
            self.progressBar.setValue(self.progressBar.value() + 1)

            # Currently running
            if len(data['currently_running']) > 0:
                # Add pkg name to list
                pkg_name_item = QtGui.QTreeWidgetItem()
                pkg_name_item.setText(0, str(data['itemlist_name']))
                self.ui.tool_pkg_list_running.insertTopLevelItem(0, pkg_name_item)
                pkg_name_item.setExpanded(True)

                # Add running binaries as children
                for proc_name in data['currently_running']:
                    proc_item = QtGui.QTreeWidgetItem()
                    proc_item.setText(0, str(proc_name))
                    pkg_name_item.insertChild(0, proc_item)

            # Negative karma
            update_item = None
            for comment in data['formatted_comments']:
                #  If we've got negative karma, show this update in list
                if comment['karma'] == -1:
                    # Prepare line with update itself
                    if  update_item is None:
                        update_item = QtGui.QTreeWidgetItem()
                        update_item.setText(0, str(data['itemlist_name']))
                        self.ui.tool_pkg_list_negative.insertTopLevelItem(0, update_item)
                        update_item.setExpanded(True)

                    # Add comment as child to update
                    line = QtGui.QTreeWidgetItem()
                    line.setText(0, comment['text'].encode('utf-8'))
                    update_item.insertChild(0, line)

            # If it is in favorited packages, update list
            if data['parsed_nvr']['name'] in self.config.favorited_packages:
                self.toolbox.update_favorite_ignored_pkg_lists()

        # Catch progress only items
        if variant == 'progress_only':
            self.progressBar.setValue(self.progressBar.value() + 1)

    def set_installed_packages(self, packages):
        self.add_status_item(str(len(packages)) + " installed packages on system")
        for pkg in packages:
            self.__installed_packages[pkg.nvr] = pkg
        #self.__installed_packages = packages

    def __load_and_set_fedora_releases(self):
        # Load fedora-release version
        packages = self.rpmTS.dbMatch('name', 'fedora-release')
        for package in packages:
            break

        # Fill in current release as first
        self.ui.releaseComboBox.addItem('Fedora ' + str(package['version']))

        # Fill combo box
        for release in self.__FEDORA_RELEASES:
            # Skip current release
            if package['version'] == release.split()[-1]:
                continue

            self.ui.releaseComboBox.addItem(release)

    def __start_pkg_worker(self):
        # Get release and put it to queue
        # Package worker will get info about it
        self.ui.loadPackagesBtn.setText('Loading packages')
        # Clear holders
        self.__installed_packages = {}
        self.installed_updates = {}
        self.available_updates = {}
        self.__process_list = []
        # Clear gui
        self.progressBar.setValue(0)
        self.__populate_pkgList()
        self.ui.tool_pkg_list_running.clear()
        releasever = self.ui.releaseComboBox.currentText().split()[-1]
        self.pkg_worker_queue.put(releasever)

    def __available_pkg_list_loading_info(self):
        release = self.ui.releaseComboBox.currentText()
        message = "Please wait... Loading all available packages. [%s]" % release
        self.ui.statusBar.showMessage(message)

    def installed_pkg_list_loading_info(self):
        release = self.ui.releaseComboBox.currentText()
        message = "Please wait... Loading all installed packages. [%s]" % release
        self.ui.statusBar.showMessage(message)

    def save_installed_pkg_list(self, pkg_object):
        message = "All installed packages has been loaded. [Fedora %s]" % pkg_object
        self.ui.statusBar.showMessage(message)
        self.ui.loadPackagesBtn.setText('Reload packages')
        self.progressBar.hide()

    def __set_search_text(self, text):
        self.ui.searchEdit.setText(str(text))

    def __decode_dict(self, dictionary, decoding='utf-8', data_type=str):
        for key in dictionary:
            if isinstance(dictionary[key], data_type):
                dictionary[key] = dictionary[key].decode(decoding)

    def __send_comment(self):
        comment = self.ui.commentEdit.toPlainText()
        karma = self.ui.karmaBox.currentText()

        # Catch all problems
        if self.__selected_pkglist_item is None:
            message = "Comment not submitted: Could not get update from testing builds"
            self.add_status_item(message)
            return
        if not self.ui.usernameEdit.text():
            message = "Please enter FAS username."
            self.add_status_item(message)
            return
        if not self.ui.passwordEdit.text():
            message = "Please enter FAS password."
            self.add_status_item(message)
            return

        # Create thread
        sender = SendKarma(self.__selected_pkglist_item,
                           self.ui.usernameEdit.text(),
                           self.ui.passwordEdit.text(),
                           comment,
                           karma,
                           self)
        sender.start()

        self.ui.sendBtn.setText('Sending in progress')
        self.ui.sendBtn.setDisabled(True)

        message = "Processing... Wait please..."
        self.add_status_item(message)

    def sending_done(self, username, password):
        # This method is called from thread SendKarma
        # It clean up after sending
        self.ui.commentEdit.clear()
        self.config.set_fas_name(username)
        self.config.set_fas_password(password)
        self.config.save_config()
        self.ui.sendBtn.setText('Send Karma')
        self.ui.sendBtn.setEnabled(True)

    def __update_can_be_shown(self, update):
        # Update need to pass several criteria to appear in current pkg list
        ## Is this package name in ignore list?
        if update['parsed_nvr']['name'] in self.config.ignored_packages:
            return False

        ## Is this pkg list for him?
        if self.ui.installedBtn.isChecked():
            if update['itemlist_name'] not in self.installed_updates:
                return False
        elif self.ui.availableBtn.isChecked():
            if update['itemlist_name'] not in self.available_updates:
                return False

        ## Does this update match search text?
        if len(self.ui.searchEdit.text()) > 0:
            if self.ui.searchEdit.text() not in update['itemlist_name']:
                return False

        ## Karma submitted filter
        if self.ui.karmaCheckBox.isChecked() and len(self.ui.karmaUsernameEdit.text()) > 0:
            karma_username = self.ui.karmaUsernameEdit.text()
            ### If there is 'not submitted' in combobox
            if re.search('not', str(self.ui.karmaFilterUserCombo.currentText())):
                for comment in update['formatted_comments']:
                    if comment['author'] == karma_username:
                        return False
            else:
                for comment in update['formatted_comments']:
                    if comment['author'] == karma_username:
                        return True
                # Not found a comment by this user?
                return False

        return True

    def __populate_pkgList(self):
        self.ui.pkgList.clear()

        # Prepare data which we want to show
        if self.ui.installedBtn.isChecked():
            pkgList = self.installed_updates
        elif self.ui.availableBtn.isChecked():
            pkgList = self.available_updates

        # Loop packages
        for key in pkgList.keys():
            update = pkgList[key]

            # Can this be shown? Send it to filters!
            if not self.__update_can_be_shown(update):
                continue

            self.ui.pkgList.addItem(update['itemlist_name'])

    def __show_installed(self):
        # Show installed packages which are from testing repo
        self.ui.availableBtn.setChecked(False)
        self.ui.installedBtn.setChecked(True)
        self.__populate_pkgList()

    def __show_available(self):
        # Show packages available to update
        self.ui.installedBtn.setChecked(False)
        self.ui.availableBtn.setChecked(True)
        self.__populate_pkgList()

    def get_bodhi_update(self, pkg=None):
        # Get bodhi update according to selected package in pkg list
        if self.ui.installedBtn.isChecked():
            pkgList = self.installed_updates
        elif self.ui.availableBtn.isChecked():
            pkgList = self.available_updates
        else:
            return

        if pkg == None:
            if self.ui.pkgList.currentItem():
                return pkgList[self.ui.pkgList.currentItem().text()]
            else:
                return self.__selected_pkglist_item
        else:
            return pkgList[pkg]

    def __show_package_detail(self, pkg_item):
        if pkg_item is None:
            return

        text_browser_string = ""

        bodhi_update = self.get_bodhi_update()

        if bodhi_update is None:
            return

        self.__selected_pkglist_item = bodhi_update

        # Show tab if not here
        self.ui.tabWidget.addTab(self.__tabBar_info_backup, 'Update: ' + bodhi_update['itemlist_name'])

        # Switch tabs to info tab
        self.ui.tabWidget.setCurrentIndex(1)

        ## title
        self.ui.pkgNameLabel.setText(bodhi_update['itemlist_name'])

        # Fill in toolbox Add package line edit
        self.ui.tool_pkg_name.setText(bodhi_update['parsed_nvr']['name'])

        ## yum info
        yum_values = {}
        yum_format_string = (
            "<b>Yum Info %(installed_version)s</b>\n"
            "<b>           Name:</b> %(name)s\n"
            "<b>      Installed:</b> %(installed_timedelta)s ago\n"
            "<b>           Arch:</b> %(arch)s\n"
            "<b>        Version:</b> %(version)s\n"
            "<b>        Release:</b> %(release)s\n"
            "<b>           Size:</b> %(size)s\n"
            "<b>           Repo:</b> %(repo)s\n"
            "<b>      From repo:</b> %(from_repo)s\n"
            "<b>        Summary:</b> %(summary)s\n"
            "<b>            URL:</b> %(url)s\n"
            "<b>        License:</b> %(license)s\n\n"
            "\n\n"
        )

        # Grab yum info from installed package
        if bodhi_update.variant == 'installed':
            yum_pkg = bodhi_update.yum_package
        elif bodhi_update.variant == 'available':
            # Search for package
            for key in self.__installed_packages.keys():
                if self.__installed_packages[key].name == bodhi_update.yum_package.name:
                    yum_pkg = self.__installed_packages[key]
                    break
        else:
            yum_pkg = None

        if yum_pkg is not None:
            # If we got yum package
            # fetch info from yum_pkg
            yum_values['name'] = yum_pkg.name
            yum_values['arch'] = yum_pkg.arch
            yum_values['version'] = yum_pkg.version
            yum_values['release'] = yum_pkg.release

            # Calculate install time
            now = datetime.datetime.now()
            install_age=(now - datetime.datetime.fromtimestamp(yum_pkg.installtime))
            if install_age.days > 0:
                yum_values['installed_timedelta'] = str(install_age.days) + " days"
            elif install_age.seconds / 60 / 60 > 2:
                yum_values['installed_timedelta'] = str(install_age.seconds / 60 / 60) + " hours"
            else:
                yum_values['installed_timedelta'] = str(install_age.seconds / 60) + " minutes"

            # Calculate package size
            if yum_pkg.packagesize > 1e6:
                # It's better to display human readable number format
                # for the package size, e.g. 1 M (as does $ yum info)
                yum_values['size'] = "{:0.1f} M".format(round(yum_pkg.packagesize/1e6, 1))
            else:
                yum_values['size'] = "{:0.1f} k".format(round(yum_pkg.packagesize/1e3, 1))
            yum_values['repo'] = yum_pkg.repo
            yum_values['from_repo'] = yum_pkg.ui_from_repo
            yum_values['summary'] = yum_pkg.summary
            yum_values['url'] = yum_pkg.url
            yum_values['license'] = yum_pkg.license
            yum_values['description'] = yum_pkg.description
            yum_values['installed_version'] = ''
            if bodhi_update.variant == 'available':
                yum_values['installed_version'] = '(installed version)'
            # Decode all strings found in yum_values to utf-8
            self.__decode_dict(yum_values)
            # Map fetched yum info on the yum_format_string
            # Add to the final browser string
            text_browser_string += yum_format_string % yum_values

            ## Related packages
            self.ui.treeWidget_related_packages.clear()

            ### Others pkgs category
            item_others = QtGui.QTreeWidgetItem()
            item_others.setText(0, "Other packages")
            self.ui.treeWidget_related_packages.insertTopLevelItem(0, item_others)
            item_others.setExpanded(True)

            for key in bodhi_update['relevant_packages']['others'].keys():
                rel_pkg = bodhi_update['relevant_packages']['others'][key]
                pkg = QtGui.QTreeWidgetItem()
                pkg.setText(0, str(rel_pkg.name))
                pkg.setToolTip(0, rel_pkg.description)
                item_others.insertChild(0, pkg)

            ### Desktop pkgs category
            item_desktop = QtGui.QTreeWidgetItem()
            item_desktop.setText(0, "Desktop packages")
            self.ui.treeWidget_related_packages.insertTopLevelItem(0, item_desktop)
            item_desktop.setExpanded(True)

            for key in bodhi_update['relevant_packages']['desktop'].keys():
                rel_pkg = bodhi_update['relevant_packages']['desktop'][key]
                pkg = QtGui.QTreeWidgetItem()
                pkg.setText(0, str(rel_pkg.name))
                pkg.setToolTip(0, rel_pkg.description)
                item_desktop.insertChild(0, pkg)

        else:
            print "Not in installed packages"

        ## Bodhi info
        bodhi_values = {}
        bodhi_format_string = (
            "<b>Bodhi Info</b>\n"
            "<b>         Status:</b> %(status)s\n"
            "<b>        Release:</b> %(release)s\n"
            "<b>      Update ID:</b> %(updateid)s\n"
            "<b>         Builds:</b> %(builds)s"
            "<b>      Requested:</b> %(request)s\n"
            "<b>         Pushed:</b> %(pushed)s\n"
            "<b> Date Submitted:</b> %(date_submitted)s\n"
            "<b>  Date Released:</b> %(date_released)s\n"
            "<b>      Submitted:</b> %(submitter)s\n"
            "<b>          Karma:</b> %(karma)s\n"
            "<b>   Stable Karma:</b> %(stable_karma)s\n"
            "<b> Unstable Karma:</b> %(unstable_karma)s\n"
        )

        bodhi_values['status'] = bodhi_update['status']
        bodhi_values['release'] = bodhi_update['release']['long_name']
        bodhi_values['updateid'] = bodhi_update['updateid']
        builds_list = bodhi_update['builds']
        if len(builds_list):
            build_num = 0
            builds_string = ""
            for build_item in builds_list:
                if not build_num:
                    # First build name
                    builds_string += "%s\n" % build_item['nvr']
                else:
                    # Second and next builds
                    builds_string += "%s%s\n" % (17 * " ", build_item['nvr'])
                build_num += 1
            bodhi_values['builds'] = builds_string
        else:
            bodhi_values['builds'] = "None"
        bodhi_values['request'] = bodhi_update['request']
        bodhi_values['pushed'] = "True" if bodhi_update['date_pushed'] else "False"
        bodhi_values['date_submitted'] = bodhi_update['date_submitted']
        bodhi_values['date_released'] = bodhi_update['date_pushed']
        bodhi_values['submitter'] = bodhi_update['submitter']
        bodhi_values['karma'] = bodhi_update['karma']
        bodhi_values['stable_karma'] = bodhi_update['stable_karma']
        bodhi_values['unstable_karma'] = bodhi_update['unstable_karma']
        bodhi_values['notes'] = bodhi_update['notes']
        bodhi_values['bodhi_url'] = bodhi_update['bodhi_url']
        # Decode all strings found in bodhi_values to utf-8
        self.__decode_dict(bodhi_values)
        # Map fetched bodhi info on the bodhi_format_string
        # Add to the final browser string
        text_browser_string += bodhi_format_string % bodhi_values

        # First descriptions
        descriptions_values = {}
        descriptions_values['yum_description'] = yum_values['description']
        descriptions_values['bodhi_notes'] = bodhi_values['notes'].replace('\n', '<br>')
        text_browser_descriptions = (
            "<b>Yum description</b></pre>"
            "%(yum_description)s<pre>\n\n"
            "<b>Bodhi update notes</b></pre>"
            "%(bodhi_notes)s<pre>\n\n"
        )
        text_browser_desc_string = text_browser_descriptions % descriptions_values

        # Set final browser string text
        self.ui.textBrowser.setHtml('<pre>' + text_browser_desc_string + text_browser_string + '</pre>')

        ## Bugs
        self.ui.treeWidget_bugs.clear()
        if bodhi_update['bugs_by_id']:
            for key in bodhi_update['bugs_by_id']:
                bug = QtGui.QTreeWidgetItem()
                bug.setText(0, str(key))
                bug.setText(1, str(bodhi_update['bugs_by_id'][key].encode('utf-8')))
                # Set tooltip with bug title
                bug.setToolTip(0, str(bodhi_update['bugs_by_id'][key].encode('utf-8')))
                bug.setToolTip(1, str(bodhi_update['bugs_by_id'][key].encode('utf-8')))
                self.ui.treeWidget_bugs.insertTopLevelItem(0, bug)

        ## Test cases
        self.ui.treeWidget_test_cases.clear()
        test_cases_list = bodhi_update['test_cases']
        if len(test_cases_list):
            for test_case_item in reversed(test_cases_list):
                tc = QtGui.QTreeWidgetItem()
                tc.setText(0, str(test_case_item))
                self.ui.treeWidget_test_cases.insertTopLevelItem(0, tc)

        ## Feedback
        self.ui.treeWidget_feedback.clear()
        comments = bodhi_update['formatted_comments']

        ## Prepare brushes
        green_brush = QtGui.QBrush(QtCore.Qt.darkGreen)
        red_brush = QtGui.QBrush(QtCore.Qt.red)

        ## Load comments
        if comments:
            for i in comments:
                comment = CustomTreeWidgetItem()
                comment_font = comment.font(1)

                # Set color and font weight for karma
                if i['karma'] == 1:
                    comment.setForeground(1, green_brush)
                    comment_font.setBold(True)
                if i['karma'] == -1:
                    comment.setForeground(1, red_brush)
                    comment_font.setBold(True)

                # Set font
                comment.setFont(1, comment_font)
                # Set vertical alignment to Top
                comment.setTextAlignment(0, QtCore.Qt.AlignTop)
                comment.setTextAlignment(1, QtCore.Qt.AlignTop)
                comment.setTextAlignment(2, QtCore.Qt.AlignTop)
                comment.setTextAlignment(3, QtCore.Qt.AlignTop)
                # Fill it with text
                comment.setText(0, str(i['ord']))
                comment.setText(1, str(i['karma']))
                comment.setText(2, i['author'])
                comment.setText(3, i['text'])
                comment.setToolTip(3, i['text'])
                self.ui.treeWidget_feedback.insertTopLevelItem(0, comment)

        # Settings
        ## Toggle radio button normal/favorite/ignored pkg
        if bodhi_update['parsed_nvr']['name'] in self.config.favorited_packages:
            self.ui.settings_pkg_cat_favorite.setChecked(True)
        elif bodhi_update['parsed_nvr']['name'] in self.config.ignored_packages:
            self.ui.settings_pkg_cat_ignored.setChecked(True)
        else:
            self.ui.settings_pkg_cat_normal.setChecked(True)


    def exit_threads(self):
        # Try to properly exit threads
        # This still need work
        if not self.pkg_worker.isRunning():
            self.pkg_worker.exit()

        for i in range(self.__BODHI_WORKERS_COUNT):
            if not self.bodhi_workers[i].isRunning():
                self.bodhi_workers[i].exit()

def main():
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    win.show()
    ret = app.exec_()
    win.dispatcher.stop()
    win.exit_threads()
    sys.exit(ret)

if __name__ == "__main__":
    main()

# vim: set expandtab ts=4 sts=4 sw=4 :
