import sys, os

from FileFormat import *
import Banners
import pefile
from TextDecorators import *
from PyQt5 import QtGui, QtCore, QtWidgets
import PyQt5

from cemu import *

import DisasmViewMode

class Bootsector(FileFormat):
    name = 'bootsector'
    priority = 4
    DisplayTypes = ['MemAddr', 'FileAddr']


    def changeAddressMode(self):
        self.DisplayTypes = self.DisplayTypes[1:] + [self.DisplayTypes[0]]

    def getAddressMode(self):
        return self.DisplayTypes[0]

    def recognize(self, dataModel):
        self.dataModel = dataModel
        if self.dataModel.getWORD(510) == 0xAA55:
            return True

        return False

    def init(self, viewMode, parent):
        self.viewMode = viewMode

        self.MZbrush = QtGui.QBrush(QtGui.QColor(128, 0, 0))
        self.greenPen = QtGui.QPen(QtGui.QColor(255, 255, 0))


        self.textDecorator = TextDecorator(viewMode)
        self.textDecorator = HighlightASCII(self.textDecorator)
        self.textDecorator = HighlightPrefix(self.textDecorator, '\x55\xAA', brush=self.MZbrush, pen=self.greenPen)

        self.viewMode.setTransformationEngine(self.textDecorator)
        self.viewMode.selector.addSelection((446,      446+1*16, QtGui.QBrush(QtGui.QColor(125, 75, 150)), 0.8), type=TextSelection.SelectionType.PERMANENT)
        self.viewMode.selector.addSelection((446+16,   446+2*16, QtGui.QBrush(QtGui.QColor(55, 125, 50)), 0.8), type=TextSelection.SelectionType.PERMANENT)
        self.viewMode.selector.addSelection((446+2*16, 446+3*16, QtGui.QBrush(QtGui.QColor(125, 75, 150)), 0.8), type=TextSelection.SelectionType.PERMANENT)
        self.viewMode.selector.addSelection((446+3*16, 446+4*16, QtGui.QBrush(QtGui.QColor(55, 125, 50)), 0.8), type=TextSelection.SelectionType.PERMANENT)

        return True

    def hintDisasm(self):
        return DisasmViewMode.Disasm_x86_16bit

    def hintDisasmVA(self, offset):
        return 0x7c00 + offset

    def disasmVAtoFA(self, va):
        return va - 0x7c00

    def getBanners(self):
        return [BootBanner(self.dataModel, self.viewMode, self), BootHeaderBanner(self.dataModel, self.viewMode, self), Banners.BottomBanner(self.dataModel, self.viewMode)]

    def _showit(self):
        if not self.w.isVisible():
            self.w.show()
        else:
            self.w.hide()

    def _g_showit(self):
        if not self.g.isVisible():
            self.g.show()
        else:
            self.g.hide()

    def F3(self):
        self.changeAddressMode()

        self._parent.update()


    def _writeData(self, w):
        Types = {0x07 : 'NTFS',
                 0x06 : 'FAT16',
                 0x0B : 'FAT32',
                 0x0F : 'Extended',
                 0x17 : 'Hidden NTFS',
                 0x82 : 'Linux Swap',
                 0x83 : 'Linux',
                 0xDE : 'Dell diagnostic'}

        # bootable
        active = None

        for i in range(4):
            b = '0x{0}'.format(self.dataModel.getBYTE(446 + i*16, asString=True))
            if b == '0x80':
                active = i

            item = QtWidgets.QTableWidgetItem(b)
            item.setTextAlignment(QtCore.Qt.AlignRight)
            if active and i == active:
                item.setTextColor(QtGui.QColor('green'))

            w.ui.tableWidget.setItem(0, i, item)

        # type
        for i in range(4):
            b = '0x{0}'.format(self.dataModel.getBYTE(446 + i*16 + 4, asString=True))
            if int(b, 16) in Types:
                b = Types[int(b,16)]

            item = QtWidgets.QTableWidgetItem(b)
            item.setTextAlignment(QtCore.Qt.AlignRight)
            if active and i == active:
                item.setTextColor(QtGui.QColor('green'))

            w.ui.tableWidget.setItem(1, i, item)

        # CHS Start
        for i in range(4):
            b = '0x{0}'.format(self.dataModel.getDWORD(446 + i*16 + 1) & 0x00FFFFFF)

            item = QtWidgets.QTableWidgetItem(b)
            item.setTextAlignment(QtCore.Qt.AlignRight)
            if active and i == active:
                item.setTextColor(QtGui.QColor('green'))

            w.ui.tableWidget.setItem(2, i, item)

        # CHS End
        for i in range(4):
            b = '0x{0}'.format(self.dataModel.getDWORD(446 + i*16 + 5) & 0x00FFFFFF)

            item = QtWidgets.QTableWidgetItem(b)
            item.setTextAlignment(QtCore.Qt.AlignRight)
            if active and i == active:
                item.setTextColor(QtGui.QColor('green'))

            w.ui.tableWidget.setItem(3, i, item)

        # LBA Start
        for i in range(4):
            b = '0x{0}'.format(self.dataModel.getDWORD(446 + i*16 + 8))

            item = QtWidgets.QTableWidgetItem(b)
            item.setTextAlignment(QtCore.Qt.AlignRight)
            if active and i == active:
                item.setTextColor(QtGui.QColor('green'))

            w.ui.tableWidget.setItem(4, i, item)

        # LBA End
        for i in range(4):
            b = '0x{0}'.format(self.dataModel.getDWORD(446 + i*16 + 0xC))

            item = QtWidgets.QTableWidgetItem(b)
            item.setTextAlignment(QtCore.Qt.AlignRight)
            if active and i == active:
                item.setTextColor(QtGui.QColor('green'))

            w.ui.tableWidget.setItem(5, i, item)

    def skip_section_up(self):
        # cursor pozition in datamodel
        off = self.viewMode.getCursorAbsolutePosition()
        x = off

        T = [0x1be, 0x1ce, 0x1de, 0x1ee]

        if off < 0x1be:
            x = 0x1be

        elif off > 0x1ee + 0x10:
            x = 0x1ee

        for t in T:
            if off > t and off < t + 0x10:
                x = t + 0x10
            elif off == t:
                if t == 0x1ee:
                    x = 0x1be
                else:
                    x = t + 0x10

        self.viewMode.goTo(x)


    def skip_section_dw(self):
        # cursor pozition in datamodel
        off = self.viewMode.getCursorAbsolutePosition()
        x = off

        T = [0x1be, 0x1ce, 0x1de, 0x1ee]

        if off < 0x1be:
            x = 0x1be

        elif off > 0x1ee + 0x10:
            x = 0x1ee

        for t in T:
            if off > t and off < t + 0x10:
                x = t
            elif off == t:
                if t == 0x1be:
                    x = 0x1ee
                else:
                    x = t - 0x10

        self.viewMode.goTo(x)

    def registerShortcuts(self, parent):
        self._parent = parent
        self.w = WHeaders(parent, self)
        self._writeData(self.w)
        shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Alt+P"), parent, self._showit, self._showit)

        self.g = MyDialogGoto(parent, self)
        self._Shortcuts += [QtWidgets.QShortcut(QtGui.QKeySequence("Alt+G"), parent, self._g_showit, self._g_showit)]
        self._Shortcuts += [QtWidgets.QShortcut(QtGui.QKeySequence("F3"), parent, self.F3, self.F3)]
        self._Shortcuts += [QtWidgets.QShortcut(QtGui.QKeySequence("["), parent, self.skip_section_dw, self.skip_section_dw)]
        self._Shortcuts += [QtWidgets.QShortcut(QtGui.QKeySequence("]"), parent, self.skip_section_up, self.skip_section_up)]



class MyDialogGoto(DialogGoto):
    def initUI(self):
        super(MyDialogGoto, self).initUI()
        self.ui.comboBox.clear()

        # for bootsector we have fileaddress/load(mem)address
        self.ui.comboBox.addItems(['FileAddress', 'MemAddress'])

        self.GoTos = {'FileAddress' : self.fa, 'MemAddress' : self.va}

    # goto address type fa/va
    def fa(self, result):
        return result

    def va(self, offset):
        if offset < 0x7c00:
            return 0

        return offset - 0x7c00

class WHeaders(QtWidgets.QDialog):
    
    def __init__(self, parent, plugin):
        super(WHeaders, self).__init__(parent)
        
        self.parent = parent
        self.plugin = plugin
        self.oshow = super(WHeaders, self).show

        root = os.path.dirname(sys.argv[0])
        self.ui = PyQt5.uic.loadUi(os.path.join(root, 'plugins', 'format', 'bootsector.ui'), baseinstance=self)

        self.initUI()

    def show(self):

        # TODO: remember position? resize plugin windows when parent resize?
        pwidth = self.parent.parent.size().width()
        pheight = self.parent.parent.size().height()

        width = self.ui.tableWidget.size().width()+15
        height = self.ui.tableWidget.size().height()+15

        self.setGeometry(pwidth - width - 15, pheight - height, width, height)
        self.setFixedSize(width, height)

        self.oshow()

    def initUI(self):      

        self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)

        shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Alt+P"), self, self.close, self.close)


class BootHeaderBanner(Banners.TopBanner):
    def __init__(self, dataModel, viewMode, plugin):
        self.plugin = plugin
        super(BootHeaderBanner, self).__init__(dataModel, viewMode)

    def draw(self):
        qp = QtGui.QPainter()
        qp.begin(self.qpix)

        qp.fillRect(0, 0, self.width,  self.height, self.backgroundBrush)
        qp.setPen(self.textPen)
        qp.setFont(self.font)

        cemu = ConsoleEmulator(qp, self.height//self.fontHeight, self.width//self.fontWidth)

        displayType = self.plugin.getAddressMode()

        offset = 1
        if displayType == 'MemAddr':
            offset = 2
        if displayType == 'FileAddr':
            offset = 1

        cemu.writeAt(offset, 0, displayType)

        offset = 11

        text = ''
        text = self.viewMode.getHeaderInfo()

        cemu.writeAt(offset, 0, text)
        
        qp.end()


class BootBanner(Banners.FileAddrBanner):
    def __init__(self, dataModel, viewMode, plugin):
        self.plugin = plugin
        super(BootBanner, self).__init__(dataModel, viewMode)

    def draw(self):
        qp = QtGui.QPainter()

        displayType = self.plugin.getAddressMode()

        if displayType == 'MemAddr':
            offset = 0x7c00 + self.viewMode.getPageOffset()
        else:
            offset = self.viewMode.getPageOffset()

        columns, rows = self.viewMode.getGeometry()

        qp.begin(self.qpix)
        qp.fillRect(0, 0, self.width,  self.height, self.backgroundBrush)
        qp.setPen(self.textPen)
        qp.setFont(self.font)

        for i in range(rows):
            s = '{0:08x}'.format(offset)
            qp.drawText(0+5, (i+1) * self.fontHeight, s)
            columns = self.viewMode.getColumnsbyRow(i)
            offset += columns


        qp.end()