'''
    UI Functionality is implemented here.
'''
import sys
if (sys.platform == 'win32'):
    if sys.version_info[0] >= 3:
        sys.path.append("C:\\Python37\\lib\\site-packages\\PyQt5")
    else:
        sys.path.append("C:\\Python27\\lib\\site-packages\\PyQt5")
try:
    from PyQt5 import QtWidgets, QtGui, QtCore
    from PyQt5.QtCore import Qt
    from .defunct.widgets import BinjaWidget
    from .defunct.widgets import *
    qtAvailable = True
except:
    qtAvailable = False

from binaryninja import *

try:
    class riprTable(QtWidgets.QTableWidget):
        '''
            Handle all QTable related code here to keep the main GUI
            class relatively clean.
        '''
        def __init__(self, emuchunks, parent=None):
            super(riprTable, self).__init__()
            self.setContextMenuPolicy(Qt.DefaultContextMenu)
            self.emuchunks = emuchunks
            
            self.setColumnCount(3)
            self.setHorizontalHeaderLabels(['Name', 'Start Address', 'End Address'])
            self.horizontalHeader().setStretchLastSection(True)
            self.verticalHeader().setVisible(False)
        
        def _get_selected_codeobj(self):
            '''
                selectedIndexes returns a QModelIndex that is passed to the QtreeWidget
                which we get the QTreeWidgetItem from, which finally gets us the class name,
                which we can get the codeobj from.
            '''
             # Grab the "Index" that is selected
            item = self.triggeredIndex
            # Find the Class name from the index
            name = self.item(item.row(), item.column()).text()
            # Open the Editor
            print ("Selected Codeobj: %s" % name)
            return self.emuchunks[name]
    
        def contextMenuEvent(self, event):
            index = self.indexAt(event.pos())
            
            self.triggeredIndex = index
            menu = QtWidgets.QMenu()
            menu.addAction("Save to File", self._saveCode)
            menu.addAction("Map Address", self._addMmap)
            menu.exec_(event.globalPos())
    
        def _addMmap(self):
            codeobj = self._get_selected_codeobj()
            addr = QtWidgets.QInputDialog().getText(self, "Binary Ninja - ripr", "Enter Address (hex)")
            try:
                addr = int(addr[0], 16)
            except: # Show error
                return 
            codeobj.add_mmap(addr)
    
        def _addData(self):
            pass
    
        def _saveCode(self):
            codeobj = self._get_selected_codeobj()
            dialog = QtWidgets.QFileDialog(None, None)
            dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
            if dialog.exec_():
                fileNames = dialog.selectedFiles()
                if len(fileNames) > 0:
                    f = open(fileNames[0], 'wb')
                    f.write(codeobj.final)
                    f.close() 
except:
    pass

try:
    class riprWidget(BinjaWidget):
        '''
            riprWidget uses BinjaDock to display its essential information.
            "Helper" Functions are also defined here for simplicity
        '''
        def __init__(self, emuchunks=None):
            self.qtAvailable = qtAvailable
            if not qtAvailable:
                return
            super(riprWidget, self).__init__('ripr')
            self.emuchunks = emuchunks
            self._table = riprTable(emuchunks=emuchunks)
    
            self.setLayout(QtWidgets.QStackedLayout())
            self.layout().addWidget(self._table)
            self.setObjectName('BNPlugin_ripr')
    
        def update_table(self, emuchunks):
            '''
                This function updates the table with new code objects received from the packager
                at the end of a "ripping" process.
            '''
            if (not self.qtAvailable):
                return
            self._table.setRowCount(len(emuchunks))
            self._table.emuchunks = emuchunks
            row = 0
            for chunkName in emuchunks:
                nameField =  QtWidgets.QTableWidgetItem(chunkName)
                nameField.setFlags(Qt.ItemIsEnabled)
    
                startAddr = QtWidgets.QTableWidgetItem('0x%.8x' % emuchunks[chunkName].startaddr)
                startAddr.setFlags(Qt.ItemIsEnabled)
    
                endAddr = QtWidgets.QTableWidgetItem('0x%.8x' % (int(emuchunks[chunkName].startaddr) + int(emuchunks[chunkName].codelen)))
                endAddr.setFlags(Qt.ItemIsEnabled)
                
                self._table.setItem(row, 0, nameField)
                self._table.setItem(row, 1, startAddr)
                self._table.setItem(row, 2, endAddr)
                row += 1
            
            self._core.show()
            self._core.selectTab(self)
            self.show()
    
    
        ### Convenience wrappers for some frequently used things
        def save_file(self, codeobj):
            fname = interaction.get_save_filename_input("[ripr] Save output")
            if fname == None:
                return
            f = open(fname, "w+")
            f.write(codeobj.final)
            f.close()
    
    
        def yes_no_box(self, msg):
            choice = interaction.show_message_box("Binary Ninja - ripr", msg, enums.MessageBoxButtonSet.YesNoButtonSet)
            if choice == enums.MessageBoxButtonResult.YesButton:
                return True
            return False

        def msgBox(self, msg):
            interaction.show_message_box("Binary Ninja - ripr", msg, enums.MessageBoxButtonSet.OKButtonSet)

    
        def text_input_box(self,msg):
            text = interaction.get_text_line_input(msg, "Binary Ninja - ripr")
            return text
    
        def impCallsOptions(self):
            msg="Code contains calls to imported functions. How should this be handled?"
            choice = interaction.get_choice_input(msg, "", ["Hook Calls", "Nop Out Calls", "Cancel"])
            if choice == 0:
                return "hook"
            if choice == 1:
                return "nop"
            if choice == 2:
                return "cancel"
except:
    # TODO Clean up/remove duplicated code
    class riprWidget(object):
        def __init__(self):
            self.qtAvailable = False
            return
        
        def update_table(self, emuchunks):
            return
        
        def save_file(self, codeobj):
            fname = interaction.get_save_filename_input("[ripr] Save output")
            if fname == None:
                return
            f = open(fname, "w+")
            f.write(codeobj.final)
            f.close()
        
        def yes_no_box(self, msg):
            choice = interaction.show_message_box("Binary Ninja - ripr", msg, enums.MessageBoxButtonSet.YesNoButtonSet)
            if choice == enums.MessageBoxButtonResult.YesButton:
                return True
            return False
        
        def msgBox(self, msg):
            interaction.show_message_box("Binary Ninja - ripr", msg, enums.MessageBoxButtonSet.OKButtonSet)

        def text_input_box(self,msg):
            text = interaction.get_text_line_input(msg, "Binary Ninja - ripr")
            return text
    
        def impCallsOptions(self):
            msg="Code contains calls to imported functions. How should this be handled?"
            choice = interaction.get_choice_input(msg, "", ["Hook Calls", "Nop Out Calls", "Cancel"])
            if choice == 0:
                return "hook"
            if choice == 1:
                return "nop"
            if choice == 2:
                return "cancel"
    pass