#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2017-2019 Random.Zebra (https://github.com/random-zebra/)
# Distributed under the MIT software license, see the accompanying
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.

from ipaddress import ip_address

from PyQt5.QtWidgets import QMessageBox

from misc import printDbg, printOK, is_hex, appendMasternode, myPopUp, myPopUp_sb
from pivx_hashlib import generate_privkey
from qt.gui_tabMNConf import TabMNConf_gui
from qt.dlg_findCollTx import FindCollTx_dlg
from threads import ThreadFuns


class TabMNConf():
    def __init__(self, caller, masternode_alias=None):
        self.caller = caller
        self.ui = TabMNConf_gui(masternode_alias)
        self.caller.tabMNConf = self.ui
        self.runInThread = ThreadFuns.runInThread
        self.spath_found = False
        self.spath = -1
        # Lookup Collateral dialog
        self.dlg = FindCollTx_dlg(self)
        # Connect GUI buttons
        self.ui.btn_genKey.clicked.connect(lambda: self.onGenerateMNkey())
        self.ui.btn_addressToSpath.clicked.connect(lambda: self.onFindSpathAndPrivKey())
        self.ui.btn_findTxid.clicked.connect(lambda: self.onLookupTx())
        self.ui.btn_editTxid.clicked.connect(lambda: self.onEditTx())
        self.ui.edt_txid.returnPressed.connect(lambda: self.onEditTx())
        self.ui.btn_cancelMNConf.clicked.connect(lambda: self.onCancelMNConfig())
        self.ui.btn_saveMNConf.clicked.connect(lambda: self.onSaveMNConf())
        self.ui.btn_spathToAddress.clicked.connect(lambda: self.spathToAddress())
        self.ui.testnetCheck.clicked.connect(lambda: self.onChangeTestnet())



    def addressToSpath(self):
        printOK("addressToSpath pressed")
        self.spath_found = False
        # Check HW device
        if self.caller.hwStatus != 2:
            myPopUp_sb(self.caller, "crit", 'SPMT - hw device check', "Connect to HW device first")
            printDbg("Unable to connect to hardware device. The device status is: %d" % self.caller.hwStatus)
            return None
        self.runInThread(self.findSpath, (0, 10), self.findSpath_done)



    def findSpath(self, ctrl, starting_spath, spath_count):
        currAddr = self.ui.edt_address.text().strip()
        currHwAcc = self.ui.edt_hwAccount.value()
        # first scan. Subsequent called by findSpath_done
        self.spath_found, self.spath = self.caller.hwdevice.scanForBip32(currHwAcc, currAddr, starting_spath, spath_count, self.isTestnet())
        printOK("Bip32 scan complete. result=%s   spath=%s" % (self.spath_found, self.spath))
        self.curr_starting_spath = starting_spath
        self.curr_spath_count = spath_count



    def findSpath_done(self):
        currAddr = self.ui.edt_address.text().strip()
        currHwAcc = self.ui.edt_hwAccount.value()
        spath = self.spath
        starting_spath = self.curr_starting_spath
        spath_count = self.curr_spath_count

        if self.spath_found:
            printOK("spath is %d" % spath)
            mess = "Found address %s in HW account %s with spath_id %s" % (currAddr, currHwAcc, spath)
            myPopUp_sb(self.caller, "info", 'SPMT - spath search', mess)
            self.ui.edt_spath.setValue(spath)
            self.findPubKey()

        else:
            mess = "Scanned addresses <b>%d</b> to <b>%d</b> of HW account <b>%d</b>.<br>" % (starting_spath, starting_spath+spath_count-1, currHwAcc)
            mess += "Unable to find the address <i>%s</i>.<br>Maybe it's on a different account.<br><br>" % currAddr
            mess += "Do you want to scan %d more addresses of account n.<b>%d</b> ?" % (spath_count, currHwAcc)
            ans = myPopUp(self.caller, "crit", 'SPMT - spath search', mess)
            if ans == QMessageBox.Yes:
                starting_spath += spath_count
                self.runInThread(self.findSpath, (starting_spath, spath_count), self.findSpath_done)



    def findPubKey(self):
        printDbg("Computing public key...")
        currSpath = self.ui.edt_spath.value()
        currHwAcc = self.ui.edt_hwAccount.value()
        # Check HW device
        if self.caller.hwStatus != 2:
            myPopUp_sb(self.caller, "crit", 'SPMT - hw device check', "Connect to HW device first")
            printDbg("Unable to connect to hardware device. The device status is: %d" % self.caller.hwStatus)
            return None

        result = self.caller.hwdevice.scanForPubKey(currHwAcc, currSpath, self.isTestnet())

        # Connection pop-up
        warningText = "Unable to find public key. The action was refused on the device or another application "
        warningText += "might have taken over the USB communication with the device.<br><br>"
        warningText += "To continue click the <b>Retry</b> button.\nTo cancel, click the <b>Abort</b> button."
        mBox = QMessageBox(QMessageBox.Critical, "WARNING", warningText, QMessageBox.Retry)
        mBox.setStandardButtons(QMessageBox.Retry | QMessageBox.Abort);

        while result is None:
            ans = mBox.exec_()
            if ans == QMessageBox.Abort:
                return
            # we need to reconnect the device
            self.caller.hwdevice.clearDevice()
            self.caller.hwdevice.initDevice(self.caller.header.hwDevices.currentIndex())

            result = self.caller.hwdevice.scanForPubKey(currHwAcc, currSpath, self.isTestnet())

        mess = "Found public key:\n%s" % result
        myPopUp_sb(self.caller, "info", "SPMT - findPubKey", mess)
        printOK("Public Key: %s" % result)
        self.ui.edt_pubKey.setText(result)



    def findRow_mn_list(self, name):
        row = 0
        while self.caller.tabMain.myList.item(row)['name'] < name:
            row += 1
        return row



    def isTestnet(self):
        return self.ui.testnetCheck.isChecked()



    def onCancelMNConfig(self):
        self.caller.tabs.setCurrentIndex(0)
        self.caller.tabs.removeTab(1)
        self.caller.mnode_to_change = None



    def onChangeTestnet(self):
        if self.isTestnet():
            self.ui.edt_masternodePort.setValue(51474)
        else:
            self.ui.edt_masternodePort.setValue(51472)



    def onEditTx(self):
        if not self.ui.edt_txid.isEnabled():
            self.ui.btn_editTxid.setText("OK")
            self.ui.edt_txid.setEnabled(True)
            self.ui.edt_txidn.setEnabled(True)
            self.ui.btn_findTxid.setEnabled(False)
            self.ui.btn_saveMNConf.setEnabled(False)

        else:
            self.ui.btn_editTxid.setText("edit")
            self.ui.edt_txid.setEnabled(False)
            self.ui.edt_txidn.setEnabled(False)
            self.ui.btn_findTxid.setEnabled(True)
            self.ui.btn_saveMNConf.setEnabled(True)



    def onFindSpathAndPrivKey(self):
        self.ui.edt_spath.setValue(0)
        self.ui.edt_pubKey.setText('')
        self.addressToSpath()



    def onLookupTx(self):
        # address check
        currAddr = self.ui.edt_address.text().strip()
        # Check rpc connection
        printDbg("Checking RPC connection")
        if not self.caller.rpcConnected:
            myPopUp_sb(self.caller, "crit", 'SPMT - hw device check', "Connect to RPC server first")
            printDbg("Unable to connect: %s" % self.caller.rpcStatusMess)
            return None
        try:
            # Update Lookup dialog
            self.dlg.load_data(currAddr)
            if self.dlg.exec_():
                txid, txidn = self.dlg.getSelection()
                self.ui.edt_txid.setText(txid)
                self.ui.edt_txidn.setValue(txidn)

        except Exception as e:
            printDbg(e)



    def onGenerateMNkey(self):
        printDbg("Generate MNkey pressed")
        reply = QMessageBox.Yes

        if self.ui.edt_mnPrivKey.text() != "":
            reply = myPopUp(self.caller, "warn", "GENERATE PRIV KEY",
                                 "Are you sure?\nThis will overwrite current private key", QMessageBox.No)

        if reply == QMessageBox.No:
            return

        newkey = generate_privkey(self.isTestnet())
        self.ui.edt_mnPrivKey.setText(newkey)



    def onSaveMNConf(self):
        try:
            if self.ui.edt_pubKey.text() == "" or self.ui.edt_txid.text() == "" or self.ui.edt_mnPrivKey.text() == "":
                mess_text = 'Attention! Complete the form before saving.<br>'
                mess_text += "<b>pubKey = </b>%s<br>" % self.ui.edt_pubKey.text()
                mess_text += "<b>txId = </b>%s<br>" % self.ui.edt_txid.text()
                mess_text += "<b>mnPrivKey = </b>%s<br>" % self.ui.edt_mnPrivKey.text()
                myPopUp_sb(self.caller, "crit", 'Complete Form', mess_text)
                return

            if not is_hex(self.ui.edt_txid.text()):
                mess_text = 'Attention! txid format is not valid.<br>'
                mess_text += "<b>txId = </b>%s<br>" % self.ui.edt_txid.text()
                mess_text += 'transaction id must be in hex format.<br>'
                myPopUp_sb(self.caller, "crit", 'Complete Form', mess_text)
                return

            # check for duplicate names
            mn_alias = self.ui.edt_name.text().strip()
            # if we are changing a masternode check for duplicate only if name is changed
            old_alias = None
            if not self.caller.mnode_to_change is None:
                old_alias = self.caller.mnode_to_change['name']
            if self.caller.isMasternodeInList(mn_alias) and old_alias != mn_alias:
                mess_text = 'Attention! The name <b>%s</b> is already in use for another masternode.<br>' % mn_alias
                mess_text += 'Choose a different name (alias) for the masternode'
                myPopUp_sb(self.caller, "crit", 'Complete Form', mess_text)
                return

            # create new item
            new_masternode = {}
            new_masternode['name'] = mn_alias
            masternodeIp = self.ui.edt_masternodeIp.text().strip()
            if not masternodeIp.endswith('.onion'):
                masternodeIp = ip_address(masternodeIp).compressed
            new_masternode['ip'] = masternodeIp
            new_masternode['port'] = self.ui.edt_masternodePort.value()
            new_masternode['mnPrivKey'] = self.ui.edt_mnPrivKey.text().strip()
            new_masternode['isTestnet'] = 0 if not self.isTestnet() else 1
            new_masternode['isHardware'] = True
            new_masternode['hwAcc'] = self.ui.edt_hwAccount.value()

            coll = {}
            coll['address'] = self.ui.edt_address.text().strip()
            coll['spath'] = self.ui.edt_spath.value()
            coll['pubKey'] = self.ui.edt_pubKey.text().strip()
            coll['txid'] = self.ui.edt_txid.text().strip()
            coll['txidn'] = self.ui.edt_txidn.value()

            new_masternode['collateral'] = coll

            # Add to cache, QListWidget and database (and remove/edit mnode_to_change)
            appendMasternode(self.caller, new_masternode)

            # go back
            self.onCancelMNConfig()

        except Exception as e:
            error_msg = "ERROR: %s" % e
            printDbg(error_msg)
            myPopUp_sb(self.caller, "crit", 'ERROR', error_msg)



    def spathToAddress(self):
        printOK("spathToAddress pressed")
        currHwAcc = self.ui.edt_hwAccount.value()
        currSpath = self.ui.edt_spath.value()
        # Check HW device
        if self.caller.hwStatus != 2:
            myPopUp_sb(self.caller, "crit", 'SPMT - hw device check', "Connect to HW device first")
            printDbg("Unable to connect to hardware device. The device status is: %d" % self.caller.hwStatus)
            return None
        addr = self.caller.hwdevice.scanForAddress(currHwAcc, currSpath, self.isTestnet())
        if addr:
            self.ui.edt_address.setText(addr)
            self.findPubKey()