from PyQt4.QtGui import *
from PyQt4.QtCore import *

from bitcoin.core import CBlockHeader, b2x, b2lx

from hashmal_lib.gui_utils import monospace_font
from hashmal_lib.core import BlockHeader, Block

class BlockHeaderModel(QAbstractTableModel):
    """Model of a block header."""
    def __init__(self, header=None, parent=None):
        super(BlockHeaderModel, self).__init__(parent)
        self.vertical_header = []
        self.set_header(header)

    def rowCount(self, parent=QModelIndex()):
        return len(self.header.fields) if self.header else len(self.vertical_header)

    def columnCount(self, parent=QModelIndex()):
        return 1

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role not in [Qt.DisplayRole, Qt.EditRole]: return None
        if orientation != Qt.Vertical or section >= len(self.vertical_header): return None
        return self.vertical_header[section]

    def setHeaderData(self, section, orientation, value, role=Qt.EditRole):
        if orientation != Qt.Vertical: return False
        try:
            self.vertical_header.insert(section, value)
        except Exception:
            return False
        self.headerDataChanged.emit(orientation, 0, section)
        return True

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid() or not self.header: return None
        if role == Qt.FontRole:
            return monospace_font
        if role not in [Qt.DisplayRole, Qt.ToolTipRole, Qt.EditRole]:
            return None

        field_name = self.header.fields[index.row()][0]
        field = self.header.fields[index.row()]
        data = getattr(self.header, field[0])
        if field[1] == 'bytes':
            data = b2lx(data)
        else:
            data = str(data)
        return data

    def set_header(self, header):
        """Reset the model to reflect blk."""
        if header.__class__ not in [BlockHeader, CBlockHeader] or header is None:
            return self.clear()
        self.beginResetModel()
        self.header = BlockHeader.from_header(header)

        self.vertical_header = []
        field_names = [i[0] for i in self.header.fields]
        for i, field in enumerate(self.header.fields):
            self.setHeaderData(i, Qt.Vertical, field[0])
        self.endResetModel()

    def clear(self):
        self.beginResetModel()
        self.header = None
        self.endResetModel()

class BlockHeaderWidget(QWidget):
    """Model and View showing a block header."""
    def __init__(self, parent=None):
        super(BlockHeaderWidget, self).__init__(parent)
        self.model = BlockHeaderModel()

        self.view = QTableView()
        self.view.setModel(self.model)
        self.view.verticalHeader().setResizeMode(QHeaderView.ResizeToContents)
        self.view.verticalHeader().setHighlightSections(False)
        self.view.horizontalHeader().setResizeMode(QHeaderView.Stretch)
        self.view.horizontalHeader().setHighlightSections(False)
        self.view.horizontalHeader().setVisible(False)

        self.model.modelReset.connect(lambda: self.view.verticalHeader().resizeSections(QHeaderView.ResizeToContents))
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.view.customContextMenuRequested.connect(self.context_menu)

        vbox = QVBoxLayout()
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.addWidget(self.view)
        self.setLayout(vbox)

    def context_menu(self, pos):
        if len(self.view.selectedIndexes()) == 0:
            return
        menu = QMenu()
        copy = menu.addMenu('Copy')
        copy.addAction('Serialized header', self.copy_serialized)

        menu.exec_(self.view.viewport().mapToGlobal(pos))

    def copy_serialized(self):
        data = b2x(self.model.header.serialize())
        QApplication.clipboard().setText(data)

    def clear(self):
        self.model.clear()

    def set_block_header(self, block):
        self.model.set_header(block)

class BlockTxsWidget(QWidget):
    """Model and View showing a block's transactions."""
    def __init__(self, parent=None):
        super(BlockTxsWidget, self).__init__(parent)
        self.model = QStandardItemModel()
        self.model.setColumnCount(1)

        self.view = QListView()
        self.view.setModel(self.model)

        vbox = QVBoxLayout()
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.addWidget(self.view)
        self.setLayout(vbox)

    def clear(self):
        self.model.clear()

    def set_block(self, block):
        self.clear()
        if not isinstance(block, Block):
            return
        txids = [b2lx(i.GetHash()) for i in block.vtx]
        items = map(lambda x: QStandardItem(x), txids)
        for item in items:
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.model.appendRow(item)

class BlockWidget(QWidget):
    """Displays the deserialized fields of a block."""
    def __init__(self, parent=None):
        super(BlockWidget, self).__init__(parent)
        self.header_widget = BlockHeaderWidget()
        self.header_widget.view.setWhatsThis('The header of the block is displayed here.')
        self.header_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
        self.txs_widget = BlockTxsWidget()
        self.txs_widget.view.setWhatsThis('The transactions in the block are displayed here.\n\nRight-click a transaction ID to access the transaction it represents.')

        splitter = QSplitter()
        splitter.setChildrenCollapsible(False)

        form = QFormLayout()
        form.setContentsMargins(0,0,0,0)
        form1 = QFormLayout()
        form2 = QFormLayout()
        for i in [form, form1, form2]:
            i.setRowWrapPolicy(QFormLayout.WrapAllRows)
        form1.addRow('Header:', self.header_widget)
        form1.setContentsMargins(0,0,5,0)
        form2.addRow('Transactions:', self.txs_widget)
        form2.setContentsMargins(5,0,0,0)
        w = QWidget()
        w.setLayout(form1)
        splitter.addWidget(w)
        w = QWidget()
        w.setLayout(form2)
        splitter.addWidget(w)

        hbox = QHBoxLayout()
        hbox.addWidget(splitter)
        form.addRow(hbox)

        self.setLayout(form)

    def clear(self):
        self.header_widget.clear()
        self.txs_widget.clear()

    def set_block(self, block_header, block):
        self.clear()
        self.header_widget.set_block_header(block_header)
        self.txs_widget.set_block(block)