# cPy - emulator for micropython machine.Pin
# Copyright by Anthony Vogelaar, Perth Scotland
# rev 0.10  2024-10-29  Initial release

from    PySide6.QtWidgets   import  QApplication, QWidget, QPushButton, QLabel
from    PySide6.QtWidgets   import  QGridLayout
from    PySide6.QtGui       import  QIcon

# ================================= GUI =========================================

class GUI(QWidget):

    def __init__(self):
        self.inputs  = []
        self.outputs = []
        self.app = QApplication([])
        super().__init__()
        self.resize(300, 100)
        self.layout   = QGridLayout(self)
        l = QLabel('Inputs');  self.layout.addWidget(l, 0, 0)
        l = QLabel('Outputs'); self.layout.addWidget(l, 1, 0)
        self.show()


    # add an input to GUI, visible as a toggleable button
    def addInput(self, caption):
        button = QPushButton(caption)
        button.setCheckable(True)
        self.inputs.append(button)
        self.layout.addWidget(button, 0, len(self.inputs))
        return button
        
        
    # add an output to the GUI as a toggleable image. Value=0 for RED
    def addOutput(self, caption, value):
        button = QPushButton(caption)
        fn_img = 'ledGreen.png' if value else 'ledRed.png' 
        button.setIcon(QIcon(fn_img))
        button.setFlat(True)
        self.outputs.append(button)
        self.layout.addWidget(button, 1, len(self.outputs))
        return button
        
        
    # Overwrite close event to terminate python
    def closeEvent(self, event):
        raise KeyboardInterrupt


gui = GUI()

# ======================================= Pin =====================================

class Pin:
    OUT         = 0
    IN          = 1
    PULL_UP     = 2
    PULL_DOWN   = 3
    IRQ_FALLING = 4
    IRQ_RISING  = 5


    # run gui process events
    @classmethod
    def qtExe(cls):
        gui.app.processEvents()


    # instantiate an in- or output    
    def __init__(self, pin_nr, mode, pull=-1, *, value=None):
        self.pin_nr = pin_nr
        self.mode   = mode
        self.active = value
        if   mode  == Pin.OUT: self.gui_obj = gui.addOutput(f'{pin_nr}', value)
        elif mode  == Pin.IN : self.gui_obj = gui.addInput(f'{pin_nr}')


    # provide an __str__ as in machine.Pin
    def __str__(self):
        return f'Pin({self.pin_nr})'
    

    # instance call is same as instance.value()
    def __call__(self, x=None):
        return self.value(x)
        
    # set value of widget to x or request value
    def value(self, x=None):
        if x == None:                                   # requesting level
            if self.mode == Pin.IN:
                self.active = self.gui_obj.isChecked()
            return self.active
        if self.mode == Pin.OUT:
            self.active = x                             # 0=RED  1=GREEN
            fn_img      = 'ledGreen.png' if x else 'ledRed.png' 
            self.gui_obj.setIcon(QIcon(fn_img))
        
        
    # same as obj.value(1)
    def on(self):
        self.value(1)
        
        
    # same as obj.value(0)
    def off(self):
        self.value(0)


    # set IRQ callback method
    def irq(self, handler=None, trigger=None):
        self.irq_handler = handler
        self.trigger     = trigger
        self.gui_obj.clicked.connect(self.onIrq)


    # forward IRQ method
    def onIrq(self):
        if self.irq_handler:
            falling = self.gui_obj.isChecked()
            if   self.trigger == Pin.IRQ_FALLING and     falling: self.irq_handler(self)
            elif self.trigger == Pin.IRQ_RISING  and not falling: self.irq_handler(self)
        
# ================================= Test ====================================

if __name__ == '__main__':

    import  os;                 os.system('clear')
    import	sys
    import	time


    def main():

        # IRQ handler
        def onInput(sender):
            print('onInput:', sender)


        inp = Pin(10, Pin.IN, Pin.PULL_UP)
        inp.irq(onInput, Pin.IRQ_RISING)
        out = Pin(1, Pin.OUT, value=1)
        while True:
            Pin.qtExe()
            time.sleep(0.2)
            if inp(): out.on()
            else    : out.off()


    try:    
        main()
    except KeyboardInterrupt:
        pass
    sys.exit()
    
# ================================= End ======================================
