from cemu import *
from PyQt5 import QtGui, QtCore, QtWidgets

import os, sys, inspect

cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0], "pefile")))
yapsy_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0], "yapsy")))
if cmd_subfolder not in sys.path:
    sys.path.insert(0, cmd_subfolder)
    sys.path.insert(0, yapsy_subfolder)    

import pefile
from TextDecorators import *
import yapsy

class Banner(object):
    def getOrientation(self):
        NotImplementedError('method not implemented.')

    def getDesiredGeometry(self):
        NotImplementedError('method not implemented.')

    def changeDisplay(self):
        return

    def setViewMode(self, viewMode):
        self.viewMode = viewMode

    def getPixmap(self):
        return self.qpix

    def _getNewPixmap(self, width, height):
        return QtGui.QPixmap(width, height)


Orientation = enum(Left=0, Bottom=1, Top=2)



class Observer(object):
    def changeViewMode(self, viewMode):
        self.setViewMode(viewMode)

class Banners(Observer):
    BOTTOM_SEPARATOR = 5

    def __init__(self):
        self._Banners = []
        self.separatorBottom = 5
        self.separatorLeft = 5
        self.separatorTop = 5

    def add(self, banner):
        self._Banners.append(banner)

    def banners(self):
        return self._Banners
        
    def getLeftOffset(self):
        offset = 0
        for banner in self._Banners:
            if banner.getOrientation() == Orientation.Left:
                offset += banner.getDesiredGeometry()
                offset += self.separatorLeft

        return offset

    def getBottomOffset(self):
        offset = 0
        for banner in self._Banners:
            if banner.getOrientation() == Orientation.Bottom:
                offset += banner.getDesiredGeometry()
                offset += self.separatorBottom

        return offset

    def getTopOffset(self):
        offset = 0
        for banner in self._Banners:
            if banner.getOrientation() == Orientation.Top:
                offset += banner.getDesiredGeometry()
                offset += self.separatorTop

        return offset

    def resize(self, width, height):
        limit = self.getBottomOffset() + self.getTopOffset()
        for banner in self._Banners:
            # banners are not resizeable actually
            if banner.getOrientation() == Orientation.Left:
                banner.resize(banner.getDesiredGeometry(), height - limit + 4) # +4 , make to look nice

            if banner.getOrientation() == Orientation.Bottom:
                banner.resize(width, banner.getDesiredGeometry())

            if banner.getOrientation() == Orientation.Top:
                banner.resize(width, banner.getDesiredGeometry())

    def setViewMode(self, viewMode):
        for banner in self._Banners:
            banner.setViewMode(viewMode)

    def draw(self, qp, offsetLeft, offsetBottom, maxY):

        for banner in self._Banners:
            if banner.getOrientation() == Orientation.Top:
                banner.draw()
                qp.drawPixmap(offsetLeft-4, offsetBottom, banner.getPixmap())
                offsetBottom += banner.getDesiredGeometry() + self.separatorLeft

        for banner in self._Banners:
            if banner.getOrientation() == Orientation.Left:
                banner.draw()
                qp.drawPixmap(offsetLeft, offsetBottom, banner.getPixmap())
                offsetLeft += banner.getDesiredGeometry() + self.separatorLeft

        # initial offset + all offsets from all banners. We are doing this because Y growns down
        offsetBottom = maxY - self.getBottomOffset() + self.BOTTOM_SEPARATOR

        for banner in self._Banners:
            if banner.getOrientation() == Orientation.Bottom:
                banner.draw()
                qp.drawPixmap(offsetLeft, offsetBottom, banner.getPixmap())
                offsetBottom += banner.getDesiredGeometry() + self.separatorBottom



class FileAddrBanner(Banner):
    def __init__(self, dataModel, viewMode):
        self.width = 0
        self.height = 0
        self.dataModel = dataModel
        self.viewMode = viewMode
        self.qpix = self._getNewPixmap(self.width, self.height)
        self.backgroundBrush = QtGui.QBrush(QtGui.QColor(0, 0, 128))        
        

        # text font
        self.font = QtGui.QFont('Terminus', 11, QtGui.QFont.Light)

        # font metrics. assume font is monospaced
        self.font.setKerning(False)
        self.font.setFixedPitch(True)
        fm = QtGui.QFontMetrics(self.font)
        self.fontWidth  = fm.width('a')
        self.fontHeight = fm.height()

        self.textPen = QtGui.QPen(QtGui.QColor(192, 192, 192), 0, QtCore.Qt.SolidLine)

    def getOrientation(self):
        return Orientation.Left

    def getDesiredGeometry(self):
        return 10 * self.fontWidth

    def setViewMode(self, viewMode):
        self.viewMode = viewMode

    def getPixmap(self):
        return self.qpix

    def _getNewPixmap(self, width, height):
        return QtGui.QPixmap(width, height)

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

        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()

    def resize(self, width, height):
        self.width = width
        self.height = height

        self.qpix = self._getNewPixmap(self.width, self.height)
        


class BottomBanner(Banner):
    def __init__(self, dataModel, viewMode):
        self.width = 0
        self.height = 0
        self.dataModel = dataModel
        self.viewMode = viewMode

        self.qpix = self._getNewPixmap(self.width, self.height)
        self.backgroundBrush = QtGui.QBrush(QtGui.QColor(0, 0, 128))        
        

        # text font
        self.font = QtGui.QFont('Consolas', 11, QtGui.QFont.Light)

        # font metrics. assume font is monospaced
        self.font.setKerning(False)
        self.font.setFixedPitch(True)
        fm = QtGui.QFontMetrics(self.font)
        self.fontWidth  = fm.width('a')
        self.fontHeight = fm.height()

        self.textPen = QtGui.QPen(QtGui.QColor(255, 255, 0), 0, QtCore.Qt.SolidLine)

    def getOrientation(self):
        return Orientation.Bottom

    def getDesiredGeometry(self):
        return 60

    def setViewMode(self, viewMode):
        self.viewMode = 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)

        dword = self.dataModel.getDWORD(self.viewMode.getCursorAbsolutePosition(), asString=True)
        if dword is None:
            dword = '----'

        sd = 'DWORD: {0}'.format(dword)

        pos = 'POS: {0:08x}'.format(self.viewMode.getCursorAbsolutePosition())



        qword = self.dataModel.getQWORD(self.viewMode.getCursorAbsolutePosition(), asString=True)
        if qword is None:
            qword = '----'
        sq = 'QWORD: {0}'.format(qword)

        byte = self.dataModel.getBYTE(self.viewMode.getCursorAbsolutePosition(), asString=True)
        if byte is None:
            byte = '-'

        sb = 'BYTE: {0}'.format(byte)

        cemu.writeAt(1,  0, pos)
        cemu.writeAt(17, 0, sd)
        cemu.writeAt(35, 0, sq)
        cemu.writeAt(62, 0, sb)

        qp.drawLine(15 * self.fontWidth + 5, 0, 15 * self.fontWidth + 5, 50)
        qp.drawLine(33 * self.fontWidth + 5, 0, 33 * self.fontWidth + 5, 50)
        qp.drawLine(59 * self.fontWidth + 5, 0, 59 * self.fontWidth + 5, 50)
        qp.drawLine(71 * self.fontWidth + 5, 0, 71 * self.fontWidth + 5, 50)

        if self.viewMode.selector.getCurrentSelection():
            u, v = self.viewMode.selector.getCurrentSelection()
            if u != v:
                pen = QtGui.QPen(QtGui.QColor(51, 153, 255), 0, QtCore.Qt.SolidLine)
                qp.setPen(pen)

                cemu.writeAt(73, 0, 'Selection: ')
                cemu.write('{0:x}:{1}'.format(u, v-u))
        else:
            pen = QtGui.QPen(QtGui.QColor(128, 128, 128), 0, QtCore.Qt.SolidLine)
            qp.setPen(pen)

            cemu.writeAt(73, 0, '<no selection>')

        """
        qp.drawLine(self.fontWidth*(len(pos) + 1) + 15, 0, self.fontWidth*(len(pos) + 1) + 15, 50)
        qp.drawLine(self.fontWidth*(len(pos + sd) + 1) + 3*15, 0, self.fontWidth*(len(pos + sd) + 1) + 3*15, 50)
        qp.drawLine(self.fontWidth*(len(pos + sd + sq) + 1) + 5*15, 0, self.fontWidth*(len(pos + sd + sq) + 1) + 5*15, 50)
        qp.drawLine(self.fontWidth*(len(pos + sd + sq + sb) + 1) + 8*15, 0, self.fontWidth*(len(pos + sd + sq + sb) + 1) + 8*15, 50)
        """
        #qp.drawLine(270, 0, 270, 50)
        #qp.drawLine(480, 0, 480, 50)
        #qp.drawLine(570, 0, 570, 50)
        """
        # position
        qp.drawText(0 + 5, self.fontHeight, pos)
        # separator
        qp.drawLine(120, 0, 120, 50)

        # dword
        qp.drawText(130 + 5, self.fontHeight, sd)
        # separator
        qp.drawLine(270, 0, 270, 50)

        # qword
        qp.drawText(280 + 5, self.fontHeight, sq)
        # separator
        qp.drawLine(480, 0, 480, 50)

        # byte
        qp.drawText(490 + 5, self.fontHeight, sb)
        # separator
        qp.drawLine(570, 0, 570, 50)
        """
        
        qp.end()

        pass
    def getPixmap(self):
        return self.qpix

    def _getNewPixmap(self, width, height):
        return QtGui.QPixmap(width, height)

    def resize(self, width, height):
        self.width = width
        self.height = height
        self.qpix = self._getNewPixmap(self.width, self.height)


class TopBanner(Banner):
    def __init__(self, dataModel, viewMode):
        self.width = 0
        self.height = 0
        self.dataModel = dataModel
        self.viewMode = viewMode

        self.qpix = self._getNewPixmap(self.width, self.height)
        self.backgroundBrush = QtGui.QBrush(QtGui.QColor(0, 0, 128))        
        

        # text font
        self.font = QtGui.QFont('Terminus', 11, QtGui.QFont.Bold)

        # font metrics. assume font is monospaced
        self.font.setKerning(False)
        self.font.setFixedPitch(True)
        fm = QtGui.QFontMetrics(self.font)
        self.fontWidth  = fm.width('a')
        self.fontHeight = fm.height()

        self.textPen = QtGui.QPen(QtGui.QColor(255, 255, 0), 0, QtCore.Qt.SolidLine)

    def getOrientation(self):
        return Orientation.Top

    def getDesiredGeometry(self):
        return 26#22

    def setViewMode(self, viewMode):
        self.viewMode = viewMode

    def draw(self):
        # i don't really like this in terms of arhitecture. We have
        # artificially introduced getHeaderInfo() in Views. Then we had one top
        # banner implemented per plugin. I will think to a better solution

        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)

        cemu.writeAt(1, 0, 'FileAddr')

        offset = 11

        text = self.viewMode.getHeaderInfo()

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

    def getPixmap(self):
        return self.qpix

    def _getNewPixmap(self, width, height):
        return QtGui.QPixmap(width, height)

    def resize(self, width, height):
        self.width = width
        self.height = height
        self.qpix = self._getNewPixmap(self.width, self.height)