import sys
import re  # Import regex module for value extraction
from PySide6 import QtWidgets, QtCore  # Ensure to import QtCore for QTimer and threading
from utils import resource_path
from MainWindow import Ui_MainWindow  # Import the generated UI
from start_up import start_up  # Import the function to initialize UI components
from comms import list_open_com_ports, send_string_to_ports, send_mdf_and_check  # Import functions from comms.py
from pyqtgraph import PlotWidget
import pyqtgraph as pg
from styles import stylesheet


#only import thr library if in pyinstaller
if getattr(sys, "frozen", False):
    import pyi_splash


#converted_value = 0  # Initialize to None or an appropriate default value

class SerialWorker(QtCore.QThread):
    data_received = QtCore.Signal(str)  # Signal to send data back to the main thread
    
   
    def __init__(self, port):
        super().__init__()
        self.port = port
        self.running = True

    def run(self):
        while self.running:
            response = send_mdf_and_check([self.port], "wei.mdf")  # Replace with actual reading logic
            if response:
                self.data_received.emit(response)  # Emit the response for processing
            self.msleep(100)  # Delay between requests to reduce load

    def stop(self):
        self.running = False
        self.wait()  # Wait for the thread to fully stop

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        
        # set graph bg
        self.graphWidget.setBackground('w')
        self.graphWidget.clear()


        
        # Apply the stylesheet
        self.setStyleSheet(stylesheet)
        
        self.adc_factor_value = 0  # Default value, will be updated in start_up
        
        # List to hold currently open ports
        self.open_ports = []
        
        # Variable to track the connected port
        self.connected_port = None
        
        # Variable to track the state of BtnInitialize
        self.cancel_mode = False
        
        # Initialize the timer
        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.send_mdf_message)  # Connect the timer to the send_mdf_message method
        self.adc_factor_value = 0
        # Initialize UI components
        start_up(
            self.LblStatusVersionNo, 
            self.BtnStartTest, 
            self.ComboBoxLang, 
            self.ComboBoxMaxSwl, 
            self.FldJtfDeviceModelNo, 
            self.FldJtfDeviceSerialNo, 
            self.FldJtfDeviceManufacturer, 
            self.FldJtfDeviceMaxCapacitySwl, 
            self.FldTestDataToleranceThreshold, 
            self.FldTestDataTestCycleTime, 
            self.FldTestConductedByCompany, 
            self.FldTestConductedByPlace, 
            self.FldTestDataDateOfTest, 
            self.FldTestDataNextTestDue,
            self.FldDataFlowDisplay,
            self.LblAdcFactor,
            self.LblPaperSize,
            self.ComboBoxTestTime,
            #self.graphWidget,
            self.LblStabilizeTesting,
            #self.main_window
            self
         )
       

        # Connect the BtnInitialize to the handle button click method
        self.BtnInitialize.clicked.connect(self.on_initialize_clicked)

        # Connect status checkbox
        self.CbStatusPortOPen.clicked.connect(self.on_status_port_open_changed)

        # Connect BtnStartTest to start the countdown
        self.BtnStartTest.clicked.connect(self.start_countdown)
        


        self.worker = None  # Initialize worker

    def on_initialize_clicked(self):
        """Event handler for BtnInitialize click."""
        # Reset FldDataFlowDisplay when initializing
        self.FldDataFlowDisplay.clear()
        self.graphWidget.clear()


        if not self.cancel_mode:
            available_ports = list_open_com_ports()
            self.open_ports = available_ports
            
            if self.open_ports:
                print("Available COM ports:", self.open_ports)
                responding_port, received_data = send_string_to_ports(self.open_ports, "wei.zrp")
                if responding_port is not None:
                    self.LblStatusComPort.setText(responding_port)
                    self.CbStatusPortOPen.setChecked(True)
                    
                    # Store the connected port
                    self.connected_port = responding_port

                    # Start the worker thread
                    self.worker = SerialWorker(responding_port)
                    self.worker.data_received.connect(self.handle_received_data)  # Connect signal
                    self.worker.start()  # Start the thread
                    
                    self.BtnStartTest.show()  # Display the start test button

                    # Change button text to "Cancel"
                    self.BtnInitialize.setText("Cancel")
                    self.cancel_mode = True  # Set the cancel mode to True
                else:
                    self.LblStatusComPort.setText("--")
                    self.CbStatusPortOPen.setChecked(False)
            else:
                self.LblStatusComPort.setText("No available ports.")
        else:
            # If already in Cancel mode, stop the worker
            self.on_cancel_clicked()

    def handle_received_data(self, response):
        """Handle data received from the serial worker."""
        self.LblStatusLiveData.setText(response)  # Update the status label
        self.update_data_flow_display(response)  # Update the display
        
        # Extract the specific value
        extracted_value = self.extract_value(response)
        if extracted_value is not None:
            # Get the currently selected option from ComboBoxMaxSwl
            selected_unit = self.ComboBoxMaxSwl.currentText()  # Gets the selected option
            # Convert the extracted value to float
            data_value = float(extracted_value)  # Ensure it's a float for calculations

            # Apply conversion factors based on the selected unit
            
            global converted_value

            if selected_unit == "kN":
                converted_value = data_value * 0.00980665
            elif selected_unit == "T":
                converted_value = data_value * 0.001
            elif selected_unit == "US tn":
                converted_value = data_value * 0.001102317
            elif selected_unit == "kg":
                converted_value = data_value  # No conversion needed
            else:
                converted_value = data_value  # Default case, if no valid unit is found

            # Set the converted value with the unit to LblValueDisplay
            converted_value = converted_value * float(self.adc_factor_value)
            display_value = f"{converted_value:.2f} {selected_unit}"    # Format to 2 decimal places
            
            self.LblValueDisplay.setText(display_value)  # Set to LblValueDisplay
            print("Converted Value:", display_value)  # Print for testing
            
            # Call toggle_live_data_radio to toggle the radio button self.toggle_live_data_radio()
            self.toggle_live_data_radio()
            
    def send_mdf_message(self):
        """This method is now redundant due to threading. Keep for future logic if needed."""
        pass  # You can choose to implement this if you need timed operation or more data handling.

    def extract_value(self, data):
        """Extract the specific numerical value from the 'wei.mdf' data."""
        match = re.search(r'GRO:\+\s*(\d+)', data)  # Using regex to match 'GRO: + <number>'
        if match:
            return match.group(1)  # Return the captured number as a string
        return None  # Return None if no match is found

    def toggle_live_data_radio(self):
        """Toggle the RbLiveData radio button state."""
        if self.RbLiveData.isChecked():
            self.RbLiveData.setChecked(False)  # Uncheck if it's currently checked
            #self.RbLiveData.setStyleSheet("background-color: none")
        else:
            self.RbLiveData.setChecked(True)   # Check it if it's currently unchecked
            #self.RbLiveData.setStyleSheet("background-color: green")
            
    def update_data_flow_display(self, data):
        """Update the FldDataFlowDisplay with new data, prepending it to the display."""
        current_display = self.FldDataFlowDisplay.toPlainText()  # Get current text in the display field
        new_display = data + "\n" + current_display  # Prepend new data
        self.FldDataFlowDisplay.setPlainText(new_display)  # Update the display
        
        

        
        if self.LblStabilizeTesting.text() == "Testing":
            
            print ("converted value ", converted_value)
            selected_datavalue_str = self.LblValueDisplay.text()
            #selected_datavalue = float(selected_datavalue_str)
            selected_datavalue = float(converted_value)
                        

            self.graphWidget.setBackground('w')
            self.graphWidget.getViewBox().setMouseEnabled(x=False, y=False)
            self.graphWidget.getViewBox().setMenuEnabled(False)
            self.graphWidget.setAntialiasing(False)
            self.graphWidget.setLabel('bottom', 'seconds', **{'color':'b', 'font-size':'14px'})
            self.graphWidget.setLabel('left', self.ComboBoxMaxSwl.currentText(), **{'color':'b', 'font-size':'14px'})

            # Set the x-axis range of the graph
            self.graphWidget.setXRange(0, 5, padding=0)
            center_value = float(converted_value)  # Convert to integer or float as needed
            fifteen_percent = converted_value * 0.15 # get a percentage value to act as an x-axis range
            
            # Set the Y-axis range centered around the extracted_value
            margin = fifteen_percent  # Define the margin to add/subtract from the center value
            self.graphWidget.setYRange(center_value - margin, center_value + margin, padding=0)
            # Plot a horizontal line at the center value
            self.x_axis_range = 100  # Fixed x-axis range
            self.x_data = [0,1,2,3,4,5]
            self.y_data = [980,980,990,1000,980,980]
            #self.curve = self.graphWidget.plot(self.x_data, self.converted_value, pen=pg.mkPen(color=(255, 0, 0)))
            
            self.graphWidget.plot(self.x_data, self.y_data, pen=pg.mkPen(color=(255, 0, 0)))
            self.graphWidget = pg.PlotWidget()     


        else:
            # Handle case where the value could not be extracted
            print("Could not extract a valid value for the Y-axis.")

    def on_cancel_clicked(self):
        """Handle canceling the operation and resetting the button."""
        if self.worker:
            self.worker.data_received.disconnect(self.handle_received_data)  # Disconnect signal
            self.worker.stop()  # Stop the worker thread
            self.worker.quit()  # Request the thread to quit
            self.worker.wait()  # Wait for the thread to finish
            self.worker = None  # Ensure worker is set to None

        self.FldDataFlowDisplay.setPlainText("")  # Clear the display immediately after stopping the worker
        self.LblStatusComPort.setText("--")  # Reset com port display
        self.BtnInitialize.setText("Initialize")  # Reset button text to Initialize
        self.CbStatusPortOPen.setChecked(False)  # Reset port open status unchecked
        self.LblValueDisplay.setText("")  # Clear the value display on cancel
        self.LblStatusLiveData.setText("")  # Clear the live data display on cancel
        self.cancel_mode = False  # Reset cancel mode
        self.BtnStartTest.hide()  # Hide the start test button
        self.LblStabilizeTesting.setText("") # clear the timer type label
        self.graphWidget.clear()


        
        
        # stop and reset timer
        self.countdown_timer.stop()
        self.LblTimerDisplay.setText("00:00")  # Stop at 00:00

    def on_status_port_open_changed(self):
        """Event handler for CbStatusPortOPen click."""
        if self.CbStatusPortOPen.isChecked():
            if self.connected_port:
                try:
                    print(f"Closing port: {self.connected_port}")
                    # Close the specific port logic should go here
                except Exception as e:
                    print(f"Error closing port {self.connected_port}: {e}")

            self.LblStatusComPort.setText("--")
            self.CbStatusPortOPen.setChecked(False)
        else:
            self.LblStatusComPort.setText("--")

    def start_countdown(self):
        #global selected_time
        self.BtnStartTest.hide()  # hide start button when testing has started
        self.LblStabilizeTesting.setText("Stabilize")
        
        # Start the countdown timer based on the selected time in ComboBoxTestTime.
        selected_time = self.ComboBoxTestTime.currentText()  # Get the selected time in mm:ss format
        selected_time = int(selected_time)
        #minutes, seconds = map(int, selected_time.split(':'))  # Split and convert to integers
        self.remaining_time = selected_time  # Convert to total seconds
        
        # Initialize the countdown timer
        self.countdown_timer = QtCore.QTimer(self)
        self.countdown_timer.timeout.connect(self.update_countdown)
        self.countdown_timer.start(1000)  # Update every second

    def update_countdown(self):
        """Update the countdown timer display."""
        if self.remaining_time > 0:
            self.remaining_time -= 1
            minutes, seconds = divmod(self.remaining_time, 60)
            self.LblTimerDisplay.setText(f"{minutes:02}:{seconds:02}")  # Update the display
            print(f"{minutes:02}:{seconds:02}")
        else:
            self.countdown_timer.stop()
            self.LblTimerDisplay.setText("00:00")  # Stop at 00:00
            
            # Check if LblStabilizeTesting is "Stabilize"
            if self.LblStabilizeTesting.text() == "Stabilize":
                self.LblStabilizeTesting.setText("Testing")  # Change to "Testing"
                
                # Restart the countdown and update graph
                self.restart_timer()

    def restart_timer(self):
        """Restart the timer"""
        
        # Reset the remaining_time based on the selected time
        selected_time = self.ComboBoxTestTime.currentText()  # Get the selected time in mm:ss format
        selected_time = int(selected_time)
        #minutes, seconds = map(int, selected_time.split(':'))  # Split and convert to integers
        self.remaining_time = selected_time  # Convert to total seconds
        # Restart the countdown timer
        self.countdown_timer.start(1000)  # Update every second

    
# Start the application
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    if getattr(sys, "frozen", False):
        pyi_splash.close()
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

please move the graphing to a new file graphing.py