import sys
import os
import serial
import serial.tools.list_ports
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, QHBoxLayout,
    QComboBox, QPushButton, QTextEdit, QLineEdit, QGroupBox, QGridLayout
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QByteArray, QTimer
from PyQt6.QtGui import QIcon, QTextCursor
from PyQt6.QtCore import QDateTime
import subprocess

# Thread to read serial data
class SerialReader(QThread):
    data_received = pyqtSignal(str)

    def __init__(self, serial_port):
        super().__init__()
        self.serial_port = serial_port
        self.running = True

    def run(self):
        while self.running and self.serial_port.is_open:
            try:
                if self.serial_port.in_waiting:
                    data = self.serial_port.readline().decode(errors='ignore')
                    self.data_received.emit(data)
            except Exception as e:
                self.data_received.emit(f"Error reading from serial: {e}")
                self.running = False

    def stop(self):
        self.running = False
        self.quit()
        self.wait()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        # Set the window's title and icon
        self.setWindowTitle("MBARI CSEM Triggerbox")
        icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MBARI_logo.png")
        self.setWindowIcon(QIcon(icon_path))

        # Central widget
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout()
        main_widget.setLayout(main_layout)

        # PC Time label and Open GUI button
        time_layout = QHBoxLayout()
        self.time_label = QLabel("PC Time: ")
        self.pc_time_label = QLabel(self.get_current_time())
        time_layout.addWidget(self.time_label)
        time_layout.addWidget(self.pc_time_label)

        # Add button to open GUI.py
        self.open_gui_button = QPushButton("Open Datalogger GUI")  # Updated button text
        self.open_gui_button.clicked.connect(self.open_gui)
        time_layout.addWidget(self.open_gui_button)

        main_layout.addLayout(time_layout)

        # COM port and baud rate dropdowns
        dropdown_layout = QHBoxLayout()
        self.port_combo = QComboBox()
        self.refresh_ports()
        dropdown_layout.addWidget(QLabel("COM Port:"))
        dropdown_layout.addWidget(self.port_combo)
        self.refresh_button = QPushButton("Refresh Portlist")
        self.refresh_button.clicked.connect(self.refresh_ports)
        dropdown_layout.addWidget(self.refresh_button)
        self.baud_combo = QComboBox()
        self.baud_combo.addItems([str(b) for b in [4800, 9600, 19200, 38400, 57600, 115200, 230400]])
        self.baud_combo.setCurrentText("115200")
        dropdown_layout.addWidget(QLabel("Baud Rate:"))
        dropdown_layout.addWidget(self.baud_combo)
        main_layout.addLayout(dropdown_layout)

        # Connect / Disconnect buttons
        button_layout = QHBoxLayout()
        self.connect_button = QPushButton("Connect")
        self.disconnect_button = QPushButton("Disconnect")
        self.disconnect_button.setEnabled(False)
        self.status_indicator = QLabel("●")
        self.status_indicator.setStyleSheet("color: red; font-size: 20px;")
        self.connect_button.clicked.connect(self.connect_serial)
        self.disconnect_button.clicked.connect(self.disconnect_serial)
        button_layout.addWidget(self.connect_button)
        button_layout.addWidget(self.disconnect_button)
        button_layout.addStretch()
        button_layout.addWidget(QLabel("Status:"))
        button_layout.addWidget(self.status_indicator)
        main_layout.addLayout(button_layout)

        # Terminal output with title
        main_layout.addWidget(QLabel("Output of Triggerbox:"))
        self.terminal_output = QTextEdit()
        self.terminal_output.setReadOnly(True)
        main_layout.addWidget(self.terminal_output)

        # Debug console
        self.debug_output = QTextEdit()
        self.debug_output.setReadOnly(True)
        self.debug_output.setStyleSheet("background-color: #2b2b2b; color: #ffffff;")
        self.debug_output.setVisible(False)  # Initially hidden
        main_layout.addWidget(self.debug_output)

        # Toggle Debug Console button
        self.toggle_debug_button = QPushButton("Toggle Debug Console")
        self.toggle_debug_button.clicked.connect(self.toggle_debug_window)
        main_layout.addWidget(self.toggle_debug_button)

        # Manual Command Input
        command_input_layout = QHBoxLayout()
        self.input_field = QLineEdit()
        self.input_field.setPlaceholderText("Enter command...")
        self.send_button = QPushButton("Send")
        self.send_button.clicked.connect(self.send_data)
        command_input_layout.addWidget(self.input_field)
        command_input_layout.addWidget(self.send_button)
        main_layout.addLayout(command_input_layout)

        # Command buttons
        command_layout = QGridLayout()
        self.add_command_button(command_layout, "View Config", "c")
        self.add_command_button(command_layout, "Get Time", "z")
        self.add_command_button(command_layout, "GPS Status", "G")
        self.add_command_button(command_layout, "Clock Status", "C")
        main_layout.addLayout(command_layout)

        # Set Frequency
        freq_layout = QHBoxLayout()
        self.freq_combo = QComboBox()
        self.freq_combo.addItems([
            "2000 (ms) = 0.5Hz",
            "1000 (ms) = 1Hz",
            "500 (ms) = 2Hz",
            "200 (ms) = 5Hz",
            "100 (ms) = 10Hz"
        ])
        self.change_freq_button = QPushButton("Set Frequency")
        self.change_freq_button.setFixedWidth(150)
        self.change_freq_button.clicked.connect(self.change_frequency)
        freq_layout.addWidget(self.change_freq_button)
        freq_layout.addWidget(self.freq_combo)
        main_layout.addLayout(freq_layout)

        # Trigger Duration
        duration_layout = QHBoxLayout()
        self.duration_input = QLineEdit()
        self.duration_input.setPlaceholderText("Enter duration (s)")
        self.change_duration_button = QPushButton("Trigger Duration")
        self.change_duration_button.setFixedWidth(150)
        self.change_duration_button.clicked.connect(self.change_duration)
        duration_layout.addWidget(self.change_duration_button)
        duration_layout.addWidget(self.duration_input)
        main_layout.addLayout(duration_layout)

        # Trigger Pause
        pause_layout = QHBoxLayout()
        self.pause_input = QLineEdit()
        self.pause_input.setPlaceholderText("Enter pause (s)")
        self.change_pause_button = QPushButton("Trigger Pause")
        self.change_pause_button.setFixedWidth(150)
        self.change_pause_button.clicked.connect(self.change_pause)
        pause_layout.addWidget(self.change_pause_button)
        pause_layout.addWidget(self.pause_input)
        main_layout.addLayout(pause_layout)

        # Log Session
        self.log_button = QPushButton("Log Session")
        self.log_button.setStyleSheet("background-color: red;")
        self.log_button.clicked.connect(self.toggle_logging)
        main_layout.addWidget(self.log_button)

        # Reset Triggerbox button
        self.reset_button = QPushButton("Reset Triggerbox")
        self.reset_button.clicked.connect(lambda: self.send_command("r"))
        main_layout.addWidget(self.reset_button)

        # Serial attributes
        self.serial_port = None
        self.reader_thread = None
        self.logging = False
        self.log_file = None

        # Timer to update the PC time every second
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)

    def add_command_button(self, layout, label, command):
        button = QPushButton(label)
        button.clicked.connect(lambda: self.send_command(command))
        layout.addWidget(button)

    def send_command(self, command):
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.write((command + "\x0A\x0D").encode())  # Control-J (LF) + Enter (CR)
            self.update_terminal(f"> {command}")
        else:
            self.update_terminal("Port not connected.")

    def send_data(self):
        if self.serial_port and self.serial_port.is_open:
            data = self.input_field.text().strip()
            if data:
                self.serial_port.write((data + "\x0A\x0D").encode())  # Control-J (LF) + Enter (CR)
                self.update_terminal(f"> {data}")
                self.input_field.clear()
        else:
            self.update_terminal("Port not connected.")

    def change_frequency(self):
        freq = self.freq_combo.currentText().split()[0]  # Extract the numeric value
        self.send_command(f"c l {freq}")

    def change_duration(self):
        duration = self.duration_input.text().strip()
        if duration:
            self.send_command(f"c d {duration}")

    def change_pause(self):
        pause = self.pause_input.text().strip()
        if pause:
            self.send_command(f"c p {pause}")

    def toggle_logging(self):
        if self.logging:
            self.logging = False
            self.log_button.setStyleSheet("background-color: red;")
            if self.log_file:
                # Append debug console content to the log file
                self.log_file.write("\n----------------\n")
                self.log_file.write(self.debug_output.toPlainText())
                self.log_file.close()
                self.log_file = None
            self.update_debug("Logging stopped.")
        else:
            self.logging = True
            self.log_button.setStyleSheet("background-color: green;")
            script_dir = os.path.dirname(os.path.abspath(__file__))
            log_filename = QDateTime.currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") + ".txt"
            log_filepath = os.path.join(script_dir, log_filename)
            self.log_file = open(log_filepath, "w")
            self.update_debug(f"Logging started: {log_filepath}")

    def get_current_time(self):
        # Format the current PC time as yyyy-dd-mm hh:mm:ss
        current_time = QDateTime.currentDateTime().toString("yyyy-dd-MM hh:mm:ss")
        return current_time

    def update_time(self):
        # Update the label with the current time
        self.pc_time_label.setText(self.get_current_time())

    def refresh_ports(self):
        self.port_combo.clear()
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combo.addItems(ports if ports else ["No ports found"])

    def connect_serial(self):
        port = self.port_combo.currentText()
        if port == "No ports found":
            self.update_terminal("No COM ports available.")
            return

        baud = int(self.baud_combo.currentText())
        try:
            self.serial_port = serial.Serial(port, baud, timeout=1)
            self.reader_thread = SerialReader(self.serial_port)
            self.reader_thread.data_received.connect(self.update_terminal)
            self.reader_thread.start()

            self.status_indicator.setStyleSheet("color: green; font-size: 20px;")
            self.connect_button.setEnabled(False)
            self.disconnect_button.setEnabled(True)
            self.update_terminal(f"Connected to {port} at {baud} baud.")
        except Exception as e:
            self.update_terminal(f"Failed to connect: {e}")

    def disconnect_serial(self):
        if self.serial_port and self.serial_port.is_open:
            self.reader_thread.stop()
            self.serial_port.close()
            self.status_indicator.setStyleSheet("color: red; font-size: 20px;")
            self.connect_button.setEnabled(True)
            self.disconnect_button.setEnabled(False)
            self.update_terminal("Disconnected.")

    def update_terminal(self, text):
        self.terminal_output.append(text)
        self.terminal_output.moveCursor(QTextCursor.MoveOperation.End)
        if self.logging and self.log_file:
            self.log_file.write(text + "\n")

    def update_debug(self, text):
        self.debug_output.append(text)
        self.debug_output.moveCursor(QTextCursor.MoveOperation.End)

    def toggle_debug_window(self):
        # Toggle visibility of the debug output
        self.debug_output.setVisible(not self.debug_output.isVisible())

    def open_gui(self):
        """Open the GUI.py script."""
        gui_path = os.path.join(os.path.dirname(__file__), "GUI.py")
        subprocess.Popen([sys.executable, gui_path])

    # Mouse events to make the window draggable
    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self._drag_pos = event.globalPosition().toPoint()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.MouseButton.LeftButton:
            delta = event.globalPosition().toPoint() - self._drag_pos
            self.move(self.pos() + delta)
            self._drag_pos = event.globalPosition().toPoint()
            event.accept()


# Run the app
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
