import sys
from PySide6 import QtCore
from PySide6.QtWidgets import QApplication, QMainWindow, QGridLayout, QWidget, QToolBar, QPushButton, QStatusBar, QFileDialog, QTableWidgetItem, QTableWidget, QAbstractItemView, QHeaderView
from PySide6.QtGui import QAction, QDesktopServices

app = QApplication(sys.argv)

class MarieGUI(QMainWindow):
    def __init__(self):
        super().__init__()

        # Setup
        self.setWindowTitle("Marie Simulator")
        self.setFixedSize(800, 600)

        # Variables
        self.filename = None
        self.M = []
        self.symbolTable = {}

        # Marie Variables
        self.marie = None
        self.marieReader = None

        # Widgets
        self.programTableWidget = None
        self.symbolTableWidget = None
        self.registerTableWidget = None
        self.memoryTableWidget = None

        # Define GUI
        self.setup()
    
    
    def setup(self):

        self.setupToolBarAndMenuBar()

        self.programTableWidget = QTableWidget()
        self.programTableWidget.setFixedSize(270, 250)
        self.programTableWidget.setStatusTip("Program Table")
        self.programTableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.programTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.symbolTableWidget = QTableWidget()
        self.symbolTableWidget.setFixedSize(220, 250)
        self.symbolTableWidget.setStatusTip("Symbol Table")
        self.symbolTableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.symbolTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.registerTableWidget = QTableWidget(7, 1, self)
        self.registerTableWidget.setStatusTip("Register Table")
        self.registerTableWidget.setHorizontalHeaderLabels(["Register", "Value"])
        self.registerTableWidget.setVerticalHeaderLabels(["AC", "PC", "MAR", "MBR", "IR", "InReg", "OutReg"])
        self.registerTableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.registerTableWidget.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.updateRegisterTableWidget()

        self.memoryTableWidget = QTableWidget(self)
        self.memoryTableWidget.setStatusTip("Memory Table")
        self.updateMemoryTableWidget()
    
        grid = QGridLayout()
        grid.addWidget(self.programTableWidget, 0, 0, 1, 1)
        grid.addWidget(self.symbolTableWidget, 0, 1, 1, 1)
        grid.addWidget(self.registerTableWidget, 0, 2, 1, 1)
        grid.addWidget(self.memoryTableWidget, 1, 0, 1, 3)
        container = QWidget()
        container.setLayout(grid)
        self.setCentralWidget(container)

    def setupToolBarAndMenuBar(self):
        toolbar = QToolBar(self)
        statusbar = QStatusBar(self)
        menu = self.menuBar()

        runButton = QPushButton("Run", self)
        stepButton = QPushButton("Step", self)
        resetButton = QPushButton("Reset", self)
        runButton.setStatusTip("Run the program")
        stepButton.setStatusTip("Step through the program - Disabled")
        resetButton.setStatusTip("Reset the Registers and Keeps the Memory")
        runButton.clicked.connect(self.guiRun)
        stepButton.clicked.connect(self.guiStep)
        resetButton.clicked.connect(self.guiReset)
        toolbar.addWidget(runButton)
        toolbar.addWidget(stepButton)
        toolbar.addWidget(resetButton)

        self.addToolBar(toolbar)
        self.setStatusBar(statusbar)

        def loadFunc():
            fileDialog = QFileDialog()
            fileDialog.setFileMode(QFileDialog.ExistingFile)
            self.fileName = fileDialog.getOpenFileName(self, "Open File", "")[0]
            reloadFunc()
        
        def reloadFunc():
            self.marieReader = MarieReader(self.fileName)
            self.marie = Marie(self.marieReader)

            self.M = self.marie.M[:]
            self.symbolTable = self.marie.symbolTable.copy()

            self.updateRegisterTableWidget()
            self.updateProgramTableWidget()
            self.updateSymbolTableWidget()
            self.updateMemoryTableWidget()

        def quitFunc():
            sys.exit(self.close())
        
        def editFunc():
            pass
        
        def saveFunc():
            pass
        
        def helpFunc():
            QDesktopServices.openUrl(QtCore.QUrl("https://github.com/dehadeaaryan/MarieSimulator/issues"))
        
        def aboutFunc():
            QDesktopServices.openUrl(QtCore.QUrl("https://github.com/dehadeaaryan/MarieSimulator/blob/main/README.md"))

        fileMenu = menu.addMenu("&File")
        fileLoad = QAction("&Load", self, shortcut="Ctrl+O")
        fileReload = QAction("&Reload", self, shortcut="Ctrl+R")
        fileClose = QAction("&Close", self)
        fileLoad.triggered.connect(loadFunc)
        fileReload.triggered.connect(reloadFunc)
        fileClose.triggered.connect(quitFunc)
        fileMenu.addActions([fileLoad, fileReload, fileClose])

        editMenu = menu.addMenu("&Edit")
        editEdit = QAction("&Edit", self)
        editSave = QAction("&Save", self)
        editEdit.triggered.connect(editFunc)
        editSave.triggered.connect(saveFunc)
        editMenu.addActions([editEdit, editSave])

        helpMenu = menu.addMenu("&Help")
        helpHelp = QAction("&Help", self)
        helpAbout = QAction("&More", self)
        helpHelp.setStatusTip("Open help")
        helpAbout.setStatusTip("About the application")
        helpHelp.triggered.connect(helpFunc)
        helpAbout.triggered.connect(aboutFunc)
        helpMenu.addActions([helpHelp, helpAbout])



    def updateProgramTableWidget(self):
        programLines = [i.split() for i in self.marieReader.input]
        self.programTableWidget.setRowCount(len(programLines))
        self.programTableWidget.setColumnCount(4)
        self.programTableWidget.setHorizontalHeaderLabels(["Label", "Opcode", "Address", "HEX"])
        self.programTableWidget.setVerticalHeaderLabels([hex(i)[2:].zfill(3).upper() for i in range(len(programLines))])
        for i in range(len(programLines)):
            ele = programLines[i]
            if ele[0][-1] != ",":
                programLines[i].insert(0, " ")
            if len(ele) == 2:
                programLines[i].insert(2, "000")
            self.programTableWidget.setItem(i, 0, QTableWidgetItem(str(programLines[i][0])))
            self.programTableWidget.setItem(i, 1, QTableWidgetItem(str(programLines[i][1])))
            self.programTableWidget.setItem(i, 2, QTableWidgetItem(str(programLines[i][2])))
            self.programTableWidget.setItem(i, 3, QTableWidgetItem(hex(self.M[i])[2:].zfill(4).upper()))

    def updateSymbolTableWidget(self):
        self.symbolTableWidget.setRowCount(len(self.symbolTable))
        self.symbolTableWidget.setColumnCount(2)
        self.symbolTableWidget.setHorizontalHeaderLabels(["Label", "Address"])
        self.symbolTableWidget.setVerticalHeaderLabels([str(i) for i in range(len(self.symbolTable))])
        for i, (label, address) in enumerate(self.symbolTable.items()):
            self.symbolTableWidget.setItem(i, 0, QTableWidgetItem(label))
            self.symbolTableWidget.setItem(i, 1, QTableWidgetItem(hex(address)[2:].zfill(3).upper()))
    
    def updateRegisterTableWidget(self):
        if self.marie == None:
            self.AC = 0
            self.PC = 0
            self.MAR = 0
            self.MBR = 0
            self.IR = 0
            self.InReg = 0
            self.OutReg =  0

            temp = [self.AC, self.PC, self.MAR, self.MBR, self.IR, self.InReg, self.OutReg]
            for i in range(7):
                self.registerTableWidget.setItem(i, 0, QTableWidgetItem(hex(temp[i])[2:].zfill(4).upper()))

        else:
            self.AC = self.marie.AC
            self.PC = self.marie.PC
            self.MAR = self.marie.MAR
            self.MBR = self.marie.MBR
            self.IR = self.marie.IR
            self.InReg = self.marie.InReg
            self.OutReg =  self.marie.OutReg
            self.M = self.marie.M

            temp = [self.AC, self.PC, self.MAR, self.MBR, self.IR, self.InReg, self.OutReg]
            for i in range(7):
                self.registerTableWidget.setItem(i, 0, QTableWidgetItem(hex(temp[i])[2:].zfill(4).upper()))
            
            self.updateMemoryTableWidget()

    def updateMemoryTableWidget(self):
        self.memoryTableWidget.setRowCount(16**2)
        self.memoryTableWidget.setColumnCount(16)
        self.memoryTableWidget.setVerticalHeaderLabels([(hex(i)[2:].zfill(2) + "0").upper() for i in range(16**2)])
        self.memoryTableWidget.setHorizontalHeaderLabels([("+" + hex(i)[2:].zfill(1)).upper() for i in range(16)])
        self.memoryTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.memoryTableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.memoryTableWidget.setAlternatingRowColors(True)
        self.M = self.marie.M if self.marie != None else self.M
        self.M += [0] * (16**3 - len(self.M))
        for i in range(16**2):
            for j in range(16):
                self.memoryTableWidget.setItem(i, j, QTableWidgetItem(hex(self.M[i*16+j])[2:].zfill(4).upper()))



    def guiRun(self):
        self.AC = self.marie.AC
        self.PC = self.marie.PC
        self.MAR = self.marie.MAR
        self.MBR = self.marie.MBR
        self.IR = self.marie.IR
        self.InReg = self.marie.InReg
        self.OutReg =  self.marie.OutReg
        self.M = self.marie.M[:]
        self.marie.run()
        self.updateRegisterTableWidget()

    def guiStep(self):
        pass
    
    def guiReset(self):
        self.marie = None
        self.updateRegisterTableWidget()
        self.updateMemoryTableWidget()
        self.marie = Marie(self.marieReader)
        

if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MarieGUI()
    window.show()

    sys.exit(app.exec())