"""
Created on 2 févr. 2014

@author: inso
"""

import logging
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QIcon, QCursor
from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QMenu, QDialog, \
                            QAbstractItemView
from cutecoin.models.identities import IdentitiesFilterProxyModel, IdentitiesTableModel
from ..gen_resources.community_tab_uic import Ui_CommunityTabWidget
from cutecoin.gui.contact import ConfigureContactDialog
from cutecoin.gui.member import MemberDialog
from .wot_tab import WotTabWidget
from .transfer import TransferMoneyDialog
from .certification import CertificationDialog
from . import toast
import asyncio
from ..tools.exceptions import LookupFailureError, NoPeerAvailable
from ..core.net.api import bma as qtbma


class CommunityTabWidget(QWidget, Ui_CommunityTabWidget):

    """
    classdocs
    """

    def __init__(self, app, account, community, password_asker, parent):
        """
        Init
        :param cutecoin.core.account.Account account: Account instance
        :param cutecoin.core.community.Community community: Community instance
        :param cutecoin.gui.password_asker.PasswordAskerDialog password_asker: Password asker dialog
        :param cutecoin.gui.currency_tab.CurrencyTabWidget parent: TabWidget instance
        :return:
        """
        super().__init__()
        self.parent = parent
        self.app = app
        self.community = community
        self.account = account
        self.password_asker = password_asker

        self.setupUi(self)

        identities_model = IdentitiesTableModel(self.community)
        proxy = IdentitiesFilterProxyModel()
        proxy.setSourceModel(identities_model)
        self.table_identities.setModel(proxy)
        self.table_identities.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table_identities.customContextMenuRequested.connect(self.identity_context_menu)
        self.table_identities.sortByColumn(0, Qt.AscendingOrder)
        self.table_identities.resizeColumnsToContents()

        self.wot_tab = WotTabWidget(self.app, self.account, self.community, self.password_asker, self)
        self.tabs_information.addTab(self.wot_tab, QIcon(':/icons/wot_icon'), self.tr("Web of Trust"))
        members_action = QAction(self.tr("Members"), self)
        members_action.triggered.connect(self.search_members)
        self.button_search.addAction(members_action)
        direct_connections = QAction(self.tr("Direct connections"), self)
        direct_connections.triggered.connect(self.search_direct_connections)
        self.button_search.addAction(direct_connections)

        self.account.identity(self.community).inner_data_changed.connect(self.handle_account_identity_change)
        self.search_direct_connections()
        self.account.membership_broadcasted.connect(self.handle_membership_broadcasted)
        self.account.revoke_broadcasted.connect(self.handle_revoke_broadcasted)
        self.account.selfcert_broadcasted.connect(self.handle_selfcert_broadcasted)
        self.refresh_quality_buttons()

    def handle_membership_broadcasted(self):
        if self.app.preferences['notifications']:
            toast.display(self.tr("Membership"), self.tr("Success sending Membership demand"))
        else:
            QMessageBox.information(self, self.tr("Membership"), self.tr("Success sending Membership demand"))

    def handle_revoke_broadcasted(self):
        if self.app.preferences['notifications']:
            toast.display(self.tr("Revoke"), self.tr("Success sending Revoke demand"))
        else:
            QMessageBox.information(self, self.tr("Revoke"), self.tr("Success sending Revoke demand"))

    def handle_selfcert_broadcasted(self):
        if self.app.preferences['notifications']:
            toast.display(self.tr("Self Certification"), self.tr("Success sending Self Certification document"))
        else:
            QMessageBox.information(self.tr("Self Certification"), self.tr("Success sending Self Certification document"))

    def identity_context_menu(self, point):
        index = self.table_identities.indexAt(point)
        model = self.table_identities.model()
        if index.row() < model.rowCount():
            source_index = model.mapToSource(index)
            pubkey_col = model.sourceModel().columns_ids.index('pubkey')
            pubkey_index = model.sourceModel().index(source_index.row(),
                                                   pubkey_col)
            pubkey = model.sourceModel().data(pubkey_index, Qt.DisplayRole)
            identity = self.app.identities_registry.lookup(pubkey, self.community)
            menu = QMenu(self)

            informations = QAction(self.tr("Informations"), self)
            informations.triggered.connect(self.menu_informations)
            informations.setData(identity)
            add_contact = QAction(self.tr("Add as contact"), self)
            add_contact.triggered.connect(self.menu_add_as_contact)
            add_contact.setData(identity)

            send_money = QAction(self.tr("Send money"), self)
            send_money.triggered.connect(self.menu_send_money)
            send_money.setData(identity)

            certify = QAction(self.tr("Certify identity"), self)
            certify.triggered.connect(self.menu_certify_member)
            certify.setData(identity)

            view_wot = QAction(self.tr("View in Web of Trust"), self)
            view_wot.triggered.connect(self.view_wot)
            view_wot.setData(identity)

            menu.addAction(informations)
            menu.addAction(add_contact)
            menu.addAction(send_money)
            menu.addAction(certify)
            menu.addAction(view_wot)

            # Show the context menu.
            menu.exec_(QCursor.pos())

    def menu_informations(self):
        person = self.sender().data()
        self.identity_informations(person)

    def menu_add_as_contact(self):
        person = self.sender().data()
        self.add_identity_as_contact({'name': person.uid,
                                    'pubkey': person.pubkey})

    def menu_send_money(self):
        person = self.sender().data()
        self.send_money_to_identity(person)

    def menu_certify_member(self):
        person = self.sender().data()
        self.certify_identity(person)

    def identity_informations(self, person):
        dialog = MemberDialog(self.account, self.community, person)
        dialog.exec_()

    def add_identity_as_contact(self, person):
        dialog = ConfigureContactDialog(self.account, self.window(), person)
        result = dialog.exec_()
        if result == QDialog.Accepted:
            self.window().refresh_contacts()

    def send_money_to_identity(self, person):
        if isinstance(person, str):
            pubkey = person
        else:
            pubkey = person.pubkey
        dialog = TransferMoneyDialog(self.account, self.password_asker)
        dialog.edit_pubkey.setText(pubkey)
        dialog.combo_community.setCurrentText(self.community.name)
        dialog.radio_pubkey.setChecked(True)
        if dialog.exec_() == QDialog.Accepted:
            currency_tab = self.window().currencies_tabwidget.currentWidget()
            currency_tab.tab_history.table_history.model().sourceModel().refresh_transfers()

    def certify_identity(self, identity):
        dialog = CertificationDialog(self.account, self.app, self.password_asker)
        dialog.combo_community.setCurrentText(self.community.name)
        dialog.edit_pubkey.setText(identity.pubkey)
        dialog.radio_pubkey.setChecked(True)
        dialog.exec_()

    def view_wot(self):
        person = self.sender().data()
        # redraw WoT with this identity selected
        self.wot_tab.draw_graph({'text': person.uid, 'id': person.pubkey})
        # change page to WoT
        index_community_tab = self.parent.tabs_account.indexOf(self)
        self.parent.tabs_account.setCurrentIndex(index_community_tab)
        index_wot_tab = self.tabs_information.indexOf(self.wot_tab)
        self.tabs_information.setCurrentIndex(index_wot_tab)

    def send_membership_demand(self):
        password = self.password_asker.exec_()
        if self.password_asker.result() == QDialog.Rejected:
            return
        asyncio.async(self.account.send_membership(password, self.community, 'IN'))

    def send_membership_leaving(self):
        reply = QMessageBox.warning(self, self.tr("Warning"),
                             self.tr("""Are you sure ?
Sending a leaving demand  cannot be canceled.
The process to join back the community later will have to be done again.""")
.format(self.account.pubkey), QMessageBox.Ok | QMessageBox.Cancel)
        if reply == QMessageBox.Ok:
            password = self.password_asker.exec_()
            if self.password_asker.result() == QDialog.Rejected:
                return

        asyncio.async(self.account.send_membership(password, self.community, 'OUT'))

    def publish_uid(self):
        reply = QMessageBox.warning(self, self.tr("Warning"),
                             self.tr("""Are you sure ?
Publishing your UID can be canceled by Revoke UID.""")
.format(self.account.pubkey), QMessageBox.Ok | QMessageBox.Cancel)
        if reply == QMessageBox.Ok:
            password = self.password_asker.exec_()
            if self.password_asker.result() == QDialog.Rejected:
                return

            try:
                self.account.send_selfcert(password, self.community)
                toast.display(self.tr("UID Publishing"),
                              self.tr("Success publishing your UID"))
            except ValueError as e:
                QMessageBox.critical(self, self.tr("Publish UID error"),
                                  str(e))
            except NoPeerAvailable as e:
                QMessageBox.critical(self, self.tr("Network error"),
                                     self.tr("Couldn't connect to network : {0}").format(e),
                                     QMessageBox.Ok)
            # except Exception as e:
            #     QMessageBox.critical(self, self.tr("Error"),
            #                          "{0}".format(e),
            #                          QMessageBox.Ok)

    def revoke_uid(self):
        reply = QMessageBox.warning(self, self.tr("Warning"),
                                 self.tr("""Are you sure ?
Revoking your UID can only success if it is not already validated by the network.""")
.format(self.account.pubkey), QMessageBox.Ok | QMessageBox.Cancel)
        if reply == QMessageBox.Ok:
            password = self.password_asker.exec_()
            if self.password_asker.result() == QDialog.Rejected:
                return

            asyncio.async(self.account.revoke(password, self.community))

    @asyncio.coroutine
    def _execute_search_text(self, text):
        response = yield from self.community.bma_access.future_request(qtbma.wot.Lookup, {'search': text})
        identities = []
        for identity_data in response['results']:
            identity = yield from self.app.identities_registry.future_lookup(identity_data['pubkey'], self.community)
            identities.append(identity)

        self_identity = self.account.identity(self.community)
        try:
            self_identity.inner_data_changed.disconnect(self.handle_account_identity_change)
            self.community.inner_data_changed.disconnect(self.handle_community_change)
        except TypeError as e:
            if "disconnect() failed" in str(e):
                pass
            else:
                raise

        self.edit_textsearch.clear()
        self.refresh_identities(identities)

    def search_text(self):
        """
        Search text and display found identities
        """
        text = self.edit_textsearch.text()

        if len(text) < 2:
            return False
        else:
            asyncio.async(self._execute_search_text(text))

    @pyqtSlot(str)
    def handle_community_change(self, origin):
        logging.debug("Handle account community {0}".format(origin))
        if origin == qtbma.wot.Members:
            self.search_members()

    @pyqtSlot(str)
    def handle_account_identity_change(self, origin):
        logging.debug("Handle account identity change {0}".format(origin))
        if origin in (str(qtbma.wot.CertifiedBy), str(qtbma.wot.CertifiersOf)):
            self.search_direct_connections()

    def search_members(self):
        """
        Search members of community and display found members
        """
        pubkeys = self.community.members_pubkeys()
        identities = []
        for p in pubkeys:
            identities.append(self.app.identities_registry.lookup(p, self.community))

        self_identity = self.account.identity(self.community)

        try:
            self_identity.inner_data_changed.disconnect(self.handle_account_identity_change)
            self.community.inner_data_changed.connect(self.handle_community_change)
        except TypeError as e:
            if "disconnect() failed" in str(e):
                pass
            else:
                raise

        self.edit_textsearch.clear()
        self.refresh_identities(identities)

    def search_direct_connections(self):
        """
        Search members of community and display found members
        """
        self_identity = self.account.identity(self.community)
        try:
            self.community.inner_data_changed.disconnect(self.handle_community_change)
            self_identity.inner_data_changed.connect(self.handle_account_identity_change)
        except TypeError as e:
            if "disconnect() failed" in str(e):
                logging.debug("Could not disconnect community")
            else:
                raise

        account_connections = []
        for p in self_identity.unique_valid_certifiers_of(self.community):
            account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community))
        certifiers_of = [p for p in account_connections]
        for p in self_identity.unique_valid_certified_by(self.community):
            account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community))
        certified_by = [p for p in account_connections
                  if p.pubkey not in [i.pubkey for i in certifiers_of]]
        identities = certifiers_of + certified_by
        self.refresh_identities(identities)

    def refresh_identities(self, identities):
        """
        Refresh the table with specified identities.
        If no identities is passed, use the account connections.
        """
        self.table_identities.model().sourceModel().refresh_identities(identities)
        self.table_identities.resizeColumnsToContents()

    def refresh_quality_buttons(self):
        try:
            if self.account.identity(self.community).published_uid(self.community):
                logging.debug("UID Published")
                if self.account.identity(self.community).is_member(self.community):
                    self.button_membership.setText(self.tr("Renew membership"))
                    self.button_membership.show()
                    self.button_publish_uid.hide()
                    self.button_leaving.show()
                    self.button_revoke_uid.hide()
                else:
                    logging.debug("Not a member")
                    self.button_membership.setText(self.tr("Send membership demand"))
                    self.button_membership.show()
                    self.button_revoke_uid.show()
                    self.button_leaving.hide()
                    self.button_publish_uid.hide()
            else:
                logging.debug("UID not published")
                self.button_membership.hide()
                self.button_leaving.hide()
                self.button_publish_uid.show()
                self.button_revoke_uid.hide()
        except LookupFailureError:
            self.button_membership.hide()
            self.button_leaving.hide()
            self.button_publish_uid.show()