"""
src/bitmessageqt/networkstatus.py
=================================

"""

import time

from PyQt4 import QtCore, QtGui

import knownnodes
import l10n
import network.stats
import shared
import widgets
from inventory import Inventory
from network.connectionpool import BMConnectionPool
from retranslateui import RetranslateMixin
from tr import _translate
from uisignaler import UISignaler


class NetworkStatus(QtGui.QWidget, RetranslateMixin):
    """Network status tab"""
    def __init__(self, parent=None):
        super(NetworkStatus, self).__init__(parent)
        widgets.load('networkstatus.ui', self)

        header = self.tableWidgetConnectionCount.horizontalHeader()
        header.setResizeMode(QtGui.QHeaderView.ResizeToContents)

        # Somehow this value was 5 when I tested
        if header.sortIndicatorSection() > 4:
            header.setSortIndicator(0, QtCore.Qt.AscendingOrder)

        self.startup = time.localtime()
        self.labelStartupTime.setText(_translate("networkstatus", "Since startup on %1").arg(
            l10n.formatTimestamp(self.startup)))

        self.UISignalThread = UISignaler.get()
        # pylint: disable=no-member
        QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
            "updateNumberOfMessagesProcessed()"), self.updateNumberOfMessagesProcessed)
        QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
            "updateNumberOfPubkeysProcessed()"), self.updateNumberOfPubkeysProcessed)
        QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
            "updateNumberOfBroadcastsProcessed()"), self.updateNumberOfBroadcastsProcessed)
        QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
            "updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateNetworkStatusTab)

        self.timer = QtCore.QTimer()

        QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds)
        # pylint: enable=no-member

    def startUpdate(self):
        """Start a timer to update counters every 2 seconds"""
        Inventory().numberOfInventoryLookupsPerformed = 0
        self.runEveryTwoSeconds()
        self.timer.start(2000)  # milliseconds

    def stopUpdate(self):
        """Stop counter update timer"""
        self.timer.stop()

    def formatBytes(self, num):
        """Format bytes nicely (SI prefixes)"""
        # pylint: disable=no-self-use
        for x in [
                _translate(
                    "networkstatus",
                    "byte(s)",
                    None,
                    QtCore.QCoreApplication.CodecForTr,
                    num),
                "kB",
                "MB",
                "GB",
        ]:
            if num < 1000.0:
                return "%3.0f %s" % (num, x)
            num /= 1000.0
        return "%3.0f %s" % (num, 'TB')

    def formatByteRate(self, num):
        """Format transfer speed in kB/s"""
        # pylint: disable=no-self-use
        num /= 1000
        return "%4.0f kB" % num

    def updateNumberOfObjectsToBeSynced(self):
        """Update the counter for number of objects to be synced"""
        self.labelSyncStatus.setText(
            _translate(
                "networkstatus",
                "Object(s) to be synced: %n",
                None,
                QtCore.QCoreApplication.CodecForTr,
                network.stats.pendingDownload() +
                network.stats.pendingUpload()))

    def updateNumberOfMessagesProcessed(self):
        """Update the counter for number of processed messages"""
        self.updateNumberOfObjectsToBeSynced()
        self.labelMessageCount.setText(
            _translate(
                "networkstatus",
                "Processed %n person-to-person message(s).",
                None,
                QtCore.QCoreApplication.CodecForTr,
                shared.numberOfMessagesProcessed))

    def updateNumberOfBroadcastsProcessed(self):
        """Update the counter for the number of processed broadcasts"""
        self.updateNumberOfObjectsToBeSynced()
        self.labelBroadcastCount.setText(
            _translate(
                "networkstatus",
                "Processed %n broadcast message(s).",
                None,
                QtCore.QCoreApplication.CodecForTr,
                shared.numberOfBroadcastsProcessed))

    def updateNumberOfPubkeysProcessed(self):
        """Update the counter for the number of processed pubkeys"""
        self.updateNumberOfObjectsToBeSynced()
        self.labelPubkeyCount.setText(
            _translate(
                "networkstatus",
                "Processed %n public key(s).",
                None,
                QtCore.QCoreApplication.CodecForTr,
                shared.numberOfPubkeysProcessed))

    def updateNumberOfBytes(self):
        """
        This function is run every two seconds, so we divide the rate of bytes
        sent and received by 2.
        """
        self.labelBytesRecvCount.setText(
            _translate(
                "networkstatus",
                "Down: %1/s  Total: %2").arg(
                    self.formatByteRate(network.stats.downloadSpeed()),
                    self.formatBytes(network.stats.receivedBytes())))
        self.labelBytesSentCount.setText(
            _translate(
                "networkstatus", "Up: %1/s  Total: %2").arg(
                    self.formatByteRate(network.stats.uploadSpeed()),
                    self.formatBytes(network.stats.sentBytes())))

    def updateNetworkStatusTab(self, outbound, add, destination):
        """Add or remove an entry to the list of connected peers"""
        # pylint: disable=too-many-branches,undefined-variable
        if outbound:
            try:
                c = BMConnectionPool().outboundConnections[destination]
            except KeyError:
                if add:
                    return
        else:
            try:
                c = BMConnectionPool().inboundConnections[destination]
            except KeyError:
                try:
                    c = BMConnectionPool().inboundConnections[destination.host]
                except KeyError:
                    if add:
                        return

        self.tableWidgetConnectionCount.setUpdatesEnabled(False)
        self.tableWidgetConnectionCount.setSortingEnabled(False)

        if add:
            self.tableWidgetConnectionCount.insertRow(0)
            self.tableWidgetConnectionCount.setItem(
                0, 0,
                QtGui.QTableWidgetItem("%s:%i" % (destination.host, destination.port))
            )
            self.tableWidgetConnectionCount.setItem(
                0, 2,
                QtGui.QTableWidgetItem("%s" % (c.userAgent))
            )
            self.tableWidgetConnectionCount.setItem(
                0, 3,
                QtGui.QTableWidgetItem("%s" % (c.tlsVersion))
            )
            self.tableWidgetConnectionCount.setItem(
                0, 4,
                QtGui.QTableWidgetItem("%s" % (",".join(map(str, c.streams))))
            )
            try:
                # .. todo:: FIXME: hard coded stream no
                rating = "%.1f" % (knownnodes.knownNodes[1][destination]['rating'])
            except KeyError:
                rating = "-"
            self.tableWidgetConnectionCount.setItem(
                0, 1,
                QtGui.QTableWidgetItem("%s" % (rating))
            )
            if outbound:
                brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern)
            else:
                brush = QtGui.QBrush(QtGui.QColor("green"), QtCore.Qt.SolidPattern)
            for j in range(1):
                self.tableWidgetConnectionCount.item(0, j).setBackground(brush)
            self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination)
            self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound)
        else:
            if len(BMConnectionPool().inboundConnections) == 0:
                self.window().setStatusIcon('yellow')
            for i in range(self.tableWidgetConnectionCount.rowCount()):
                if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination:
                    continue
                if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound:
                    self.tableWidgetConnectionCount.removeRow(i)
                    break

        self.tableWidgetConnectionCount.setUpdatesEnabled(True)
        self.tableWidgetConnectionCount.setSortingEnabled(True)
        self.labelTotalConnections.setText(
            _translate(
                "networkstatus", "Total Connections: %1").arg(
                    str(self.tableWidgetConnectionCount.rowCount())))
        # FYI: The 'singlelistener' thread sets the icon color to green when it
        # receives an incoming connection, meaning that the user's firewall is
        # configured correctly.
        if self.tableWidgetConnectionCount.rowCount() and shared.statusIconColor == 'red':
            self.window().setStatusIcon('yellow')
        elif self.tableWidgetConnectionCount.rowCount() == 0 and shared.statusIconColor != "red":
            self.window().setStatusIcon('red')

    # timer driven
    def runEveryTwoSeconds(self):
        """Updates counters, runs every 2 seconds if the timer is running"""
        self.labelLookupsPerSecond.setText(_translate("networkstatus", "Inventory lookups per second: %1").arg(
            str(Inventory().numberOfInventoryLookupsPerformed / 2)))
        Inventory().numberOfInventoryLookupsPerformed = 0
        self.updateNumberOfBytes()
        self.updateNumberOfObjectsToBeSynced()

    def retranslateUi(self):
        super(NetworkStatus, self).retranslateUi()
        self.labelStartupTime.setText(_translate("networkstatus", "Since startup on %1").arg(
            l10n.formatTimestamp(self.startup)))
