import sys
import time
import canopen
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QHBoxLayout, QLabel, QGroupBox, QFormLayout
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QColor

# Connect to CAN network
network = canopen.Network()
network.connect(channel='PCAN_USBBUS1', bustype='pcan', bitrate=250000)
motorBoardEdsPath = r'C:\Users\vineetagupta\Documents\NorthStar-Motor-Valve-Board-Firmware\MotorValveBoard\coappl\motorcontrollervalve.eds'
puBoardEdsPath = r'C:\Users\vineetagupta\Documents\NorthStar-Production-Unit-Board-Firmware\ProcessBoardV1\coappl\processBoard_1.eds'

nodes = {}
motorBoardTpdoData = {}  # Store latest setpoint and feedback per node
PuBoardData = {}

def PUBoardNode():
    global PuBoardData
    try:
        PuBoardData = canopen.RemoteNode(0x1, puBoardEdsPath)
        network.add_node(PuBoardData)
        time.sleep(0.1)

        # Add to node list early for visibility
        nodes[1] = PuBoardData

        # Now check if extra node is OPERATIONAL
        if PuBoardData.nmt.state == 'OPERATIONAL':
            PuBoardData.tpdo.read()
            PuBoardData.tpdo[1].enabled = True

            def make_extra_callback():
                def cb(map_obj):
                    global PuBoardData
                    for var in map_obj:
                        # Flow meter data (0x6005, 0x01 to 0x04)
                        if var.index == 0x6005:
                            if var.subindex == 0x01:
                                PuBoardData['flowmeter']['0x01'] = var.raw
                            elif var.subindex == 0x02:
                                PuBoardData['flowmeter']['0x02'] = var.raw
                            elif var.subindex == 0x03:
                                PuBoardData['flowmeter']['0x03'] = var.raw
                            elif var.subindex == 0x04:
                                PuBoardData['flowmeter']['0x04'] = var.raw

                        # Pressure sensor data (0x6006, 0x01 to 0x04)
                        elif var.index == 0x6006:
                            if var.subindex == 0x01:
                                PuBoardData['pressure']['0x01'] = var.raw
                            elif var.subindex == 0x02:
                                PuBoardData['pressure']['0x02'] = var.raw
                            elif var.subindex == 0x03:
                                PuBoardData['pressure']['0x03'] = var.raw
                            elif var.subindex == 0x04:
                                PuBoardData['pressure']['0x04'] = var.raw

                return cb

            PuBoardData.tpdo[1].add_callback(make_extra_callback())
        else:
            pass

    except Exception as e:
        print(f"Extra Node {1} error: {e}")


def motorNode():
    for node_id in range(5, 27):
        try:
            node = canopen.RemoteNode(node_id, motorBoardEdsPath)
            network.add_node(node)
            time.sleep(0.1)

            # Add to node list early so it's visible in GUI
            nodes[node_id] = node
            motorBoardTpdoData[node_id] = {'setpoint': '-', 'feedback': '-'}

            # Now check if node is OPERATIONAL
            if node.nmt.state == 'OPERATIONAL':
                node.tpdo.read()
                node.tpdo[1].enabled = True

                def make_callback(nid):
                    def cb(map_obj):
                        for var in map_obj:
                            if var.index == 0x6002:
                                motorBoardTpdoData[nid]['setpoint'] = var.raw
                            elif var.index == 0x6004:
                                motorBoardTpdoData[nid]['feedback'] = var.raw
                    return cb

                node.tpdo[1].add_callback(make_callback(node_id))
            else:
                pass

        except Exception as e:
            print(f"Node {node_id} error: {e}")


# --------------------------- PyQt GUI -------------------------------
class NodeTableWindow(QWidget):
    def __init__(self, nodes):
        super().__init__()
        self.setWindowTitle("Valve Node Status")
        self.nodes = nodes
        self.num_nodes = len(nodes)
        self.init_ui()
        self.start_timer()

    def init_ui(self):
        layout = QHBoxLayout(self)

        # Left side - Table
        self.table_widget = QWidget(self)
        table_layout = QVBoxLayout(self.table_widget)

        # Heading for the table
        table_heading = QLabel("Valve Details", self)
        table_layout.addWidget(table_heading)

        # Table
        self.table = QTableWidget(self.num_nodes, 4)
        self.table.setHorizontalHeaderLabels(["Node ID", "Setpoint", "Feedback", "Status"])

        # Dynamic height based on the number of nodes
        self.table.setFixedHeight(30 * self.num_nodes)  # 30px height per row
        self.table.setFixedWidth(500)

        # Adjust column width to content
        self.table.resizeColumnsToContents

        for row, node_id in enumerate(sorted(self.nodes.keys())):
            item = QTableWidgetItem(str(node_id))
            item.setTextAlignment(Qt.AlignCenter)
            self.table.setItem(row, 0, item)

        table_layout.addWidget(self.table)
        self.table_widget.setLayout(table_layout)

        # Right side - Pump, Flowmeter, and Pressure Sensor Details
        self.details_widget = QWidget(self)
        details_layout = QVBoxLayout(self.details_widget)

        # Pump Details
        pump_group = QGroupBox("Pump Details", self)
        pump_layout = QFormLayout(pump_group)
        pump_speed_label = QLabel("Speed: 0 RPM", self)  # Example pump speed
        pump_layout.addRow("Pump 1", pump_speed_label)
        pump_layout.addRow("Pump 2", pump_speed_label)
        pump_layout.addRow("Pump 3", pump_speed_label)
        pump_layout.addRow("Pump 4", pump_speed_label)
        details_layout.addWidget(pump_group)

        # Flow Meter Details
        flow_group = QGroupBox("Flow Meter Details", self)
        flow_layout = QFormLayout(flow_group)
        self.flow_rate_label = QLabel("Flow Rate: 0 L/min", self)  # Assign object name
        self.flow_rate_label.setObjectName("Flow Rate")
        flow_layout.addRow("Flow Rate", self.flow_rate_label)
        details_layout.addWidget(flow_group)

        # Pressure Sensor Details
        pressure_group = QGroupBox("Pressure Sensor Details", self)
        pressure_layout = QFormLayout(pressure_group)
        self.pressure_value_label = QLabel("Pressure: 0 bar", self)  # Assign object name
        self.pressure_value_label.setObjectName("Pressure")
        pressure_layout.addRow("Pressure", self.pressure_value_label)
        details_layout.addWidget(pressure_group)

        self.details_widget.setLayout(details_layout)

        # Add both widgets (table and details) to the main layout
        layout.addWidget(self.table_widget)
        layout.addWidget(self.details_widget)

        self.setLayout(layout)

        # Set window size
        screen = QApplication.primaryScreen()
        screen_geometry = screen.availableGeometry()
        width = int(screen_geometry.width() * 0.4)
        height = int(screen_geometry.height() * 0.7)
        x = int((screen_geometry.width() - width) / 2)
        y = int((screen_geometry.height() - height) / 2)
        self.setGeometry(x, y, width, height)


    def start_timer(self):
        self.timer = QTimer()
        self.timer.setInterval(500)
        self.timer.timeout.connect(self.update_table)
        self.timer.start()

    def update_table(self):
        for row, node_id in enumerate(sorted(self.nodes.keys())):
            node = self.nodes[node_id]

            # Node status
            try:
                status = node.nmt.state
            except:
                status = "UNKNOWN"

            # Color Node ID based on status
            node_item = self.table.item(row, 0)
            node_item.setBackground(QColor("green") if status == "OPERATIONAL" else QColor("red"))

            # Setpoint
            set_val = str(motorBoardTpdoData.get(node_id, {}).get('setpoint', '-'))
            setpoint_item = QTableWidgetItem(set_val)
            setpoint_item.setTextAlignment(Qt.AlignCenter)
            self.table.setItem(row, 1, setpoint_item)

            # Feedback
            fb_val = str(motorBoardTpdoData.get(node_id, {}).get('feedback', '-'))
            feedback_item = QTableWidgetItem(fb_val)
            feedback_item.setTextAlignment(Qt.AlignCenter)
            self.table.setItem(row, 2, feedback_item)

            # Status
            status_item = QTableWidgetItem(status)
            status_item.setTextAlignment(Qt.AlignCenter)
            self.table.setItem(row, 3, status_item)


def main():
    # Initialize all the nodes
    motorNode()
    PUBoardNode()

    # Start the GUI
    app = QApplication(sys.argv)
    window = NodeTableWindow(nodes)
    window.show()

    sys.exit(app.exec_())

if __name__ == "__main__":
    main()
