import sys
import serial
import time
import openpyxl
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QLabel,
                             QVBoxLayout, QHBoxLayout, QWidget, QFrame, QComboBox, QFileDialog, QMessageBox, QGridLayout)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon
from openpyxl.styles import PatternFill

class IndustrialCncGui(QMainWindow):
    
    def _init_(self):
        super()._init_()
        self.init_ui()
        self.init_serial('/dev/cu.usbserial-10', 115200)  # Update the port
        self.is_processing = False  # Flag to control processing state
        
    def init_ui(self):
        self.setWindowTitle('Industrial Paintball CNC Controller')
        self.setGeometry(100, 100, 1280, 720)
        self.setup_ui_elements()
        self.style_interface()
        
    def init_serial(self, port, baud_rate):
        try:
            self.ser = serial.Serial(port, baud_rate)
            time.sleep(2)  # Allow time for the connection to initialize
            print(f"Connected to the Arduino on {port}.")
        except serial.SerialException as e:
            QMessageBox.critical(self, "Serial Connection Error", f"Failed to connect to the Arduino: {e}")
            self.ser = None

    def setup_ui_elements(self):
        self.setWindowTitle('Industrial Paintball CNC Controller')
        self.setGeometry(100, 100, 1280, 720)

        main_layout = QHBoxLayout()
        left_layout = QVBoxLayout()
        right_layout = QVBoxLayout()

        self.load_button = QPushButton('Load Excel File')
        self.load_button.setFixedSize(200, 40)
        self.layer_combobox = QComboBox()
        self.layer_preview = QLabel('Layer Preview')
        self.layer_preview.setFixedSize(300, 300)
        self.layer_preview.setStyleSheet("background-color: gray")
        self.process_button = QPushButton('Process Layer')
        self.process_button.setFixedSize(200, 40)
        self.process_button.setStyleSheet("background-color: green; color: white")

        left_layout.addWidget(self.load_button)
        left_layout.addWidget(self.layer_combobox)
        left_layout.addWidget(self.layer_preview)
        left_layout.addWidget(self.process_button)

        self.create_cnc_control_buttons(right_layout)

        main_layout.addLayout(left_layout)
        main_layout.addLayout(right_layout)

        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

        self.load_button.clicked.connect(self.load_excel_file)
        self.process_button.clicked.connect(self.process_layer)

    def create_cnc_control_buttons(self, layout):
        self.shoot_button = QPushButton('SHOOT')
        self.shoot_button.clicked.connect(lambda: self.send_gcode_command("G91\nG0 F1000"))
        self.home_button = QPushButton('HOME')
        self.home_button.clicked.connect(self.move_home)

        self.stop_button = QPushButton('STOP')
        self.stop_button.clicked.connect(self.stop_processing)
        layout.addWidget(self.stop_button)  # Example command, adjust as needed
        self.up_button = QPushButton('UP')
        self.up_button.clicked.connect(lambda: self.send_gcode_command("G91\nG0 Y20 Z15"))
        self.down_button = QPushButton('DOWN')
        self.down_button.clicked.connect(lambda: self.send_gcode_command("G91\nG0 Y-20 Z-15"))
        self.left_button = QPushButton('LEFT')
        self.left_button.clicked.connect(lambda: self.send_gcode_command("G91\nG0 X-20"))
        self.right_button = QPushButton('RIGHT')
        self.right_button.clicked.connect(lambda: self.send_gcode_command("G91\nG0 X20"))

        layout.addWidget(self.shoot_button)
        layout.addWidget(self.home_button)
        layout.addWidget(self.up_button)
        layout.addWidget(self.down_button)
        layout.addWidget(self.left_button)
        layout.addWidget(self.right_button)

    def send_gcode_command(self, command, delay=1):
        if self.ser and self.ser.isOpen():
            full_command = f"{command}\n"
            print(f"Sending: {command}")
            self.ser.write(full_command.encode('utf-8'))
            self.ser.flush()
            response = self.ser.readline().decode('utf-8').strip()
            print(f"Received response: {response}")
            time.sleep(delay)  # Delay after sending the command
        else:
            QMessageBox.warning(self, "Connection Error", "Serial connection is not established.")

    def mock_shoot_action(self):
        """Simulate shooting action."""
        print("Simulating SHOOT action.")
        # Use a command suitable for your setup; here's a placeholder
        self.send_gcode_command("M300 S30", 10)  # Adjust as needed# Simulate the shoot action with a command
    
    def move_home(self):
        """Move the CNC to the origin (0,0) and reset the Z-axis."""
        self.send_gcode_command("G90\nG0 X0 Y0 Z0", 1)  # Use G90 to ensure absolute positioning
        print("Moved to HOME position.")

    def stop_processing(self):
        """Stop the current processing."""
        self.is_processing = False

    def load_excel_file(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Open Excel File", "", "Excel Files (*.xlsx)")
        if file_name:
            self.excel_file = file_name
            workbook = openpyxl.load_workbook(file_name)
            sheet_names = workbook.sheetnames
            self.layer_combobox.clear()
            self.layer_combobox.addItems(sheet_names)
   
    def process_layer(self):
        self.move_home()  # Ensure starting from the origin for each layer
        self.is_processing = True

        selected_layer = self.layer_combobox.currentText()
        if not selected_layer or self.ser is None or not self.ser.isOpen():
            QMessageBox.warning(self, "Error", "Please select a layer and ensure the device is connected.")
            return

        workbook = openpyxl.load_workbook(self.excel_file, read_only=True)
        sheet = workbook[selected_layer]
        for row in range(1, 21):
            for col in range(1, 21):
                if not self.is_processing:  # Allows for stopping the loop
                    return
                cell = sheet.cell(row, col)
                # Safely check for cell's fill color
                fill_color = cell.fill.start_color.index if cell.fill and cell.fill.start_color else '00000000'
                if fill_color == '00000000':  # Skip cells with default fill color
                    continue
                else:  # Cells with a fill color other than '00000000'
                    # Use absolute positioning for each move
                    self.send_gcode_command(f"G90\nG0 X{col-1} Y{row-1} Z{row-1}", 1)
                    self.mock_shoot_action()  # Simulate shooting



    def style_interface(self):
        self.setStyleSheet("""
        QMainWindow {
            background-color: #2c3e50;
        }
        QPushButton {
            background-color: #3498db;
            color: white;
            border-style: outset;
            border-width: 2px;
            border-radius: 10px;
            border-color: beige;
            font: bold 14px;
            padding: 6px;
            min-width: 10em;
            margin: 4px;
        }
        QLabel {
            background-color: #95a5a6;
            color: white;
            font: bold 14px;
            border-radius: 5px;
            padding: 6px;
            margin: 4px;
        }
        QComboBox {
            background-color: #34495e;
            color: white;
            border-radius: 5px;
            padding: 6px;
            margin: 4px;
            min-width: 10em;
        }
        """)

def main():
    app = QApplication(sys.argv)
    gui = IndustrialCncGui()
    gui.show()
    sys.exit(app.exec_())

if _name_ == '_main_':
    main()