# File: components/i2c/i2c_polling.py

from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout
from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtGui import QPainter, QColor, QPen
from components.console.console_registry import log_main_console
from smbus2 import SMBus
import config.config as config
import math

AS5048_ADDR = 0x40
REG_ANGLE_MSB = 0xFE
REG_ANGLE_LSB = 0xFF


class AngleGauge(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMinimumSize(200, 200)
        self.angle = 0  # degrees

    def set_angle(self, angle):
        self.angle = max(0.0, min(90.0, angle))
        self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)

        rect = self.rect()
        width = rect.width()
        height = rect.height()
        center_x = width // 2
        bottom_y = height - 10
        length = height - 40

        # Draw vertical Y axis
        pen = QPen(QColor(180, 180, 180), 2)
        painter.setPen(pen)
        painter.drawLine(center_x, bottom_y, center_x, bottom_y - length)

        # Draw needle
        radians = math.radians(self.angle)
        end_x = int(center_x + length * math.sin(radians))
        end_y = int(bottom_y - length * math.cos(radians))

        pen = QPen(QColor(0, 100, 200), 4)
        painter.setPen(pen)
        painter.drawLine(center_x, bottom_y, end_x, end_y)

        # Draw angle value
        painter.setPen(Qt.GlobalColor.black)
        painter.drawText(
            rect,
            int(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter),
            f"{self.angle:.1f}°",
        )


class I2CPollingWidget(QWidget):
    def __init__(self, bus_id=1, parent=None):
        super().__init__(parent)

        self.label = QLabel("AS5048A Angle Sensor")
        self.gauge = AngleGauge()
        self.zero_raw = None

        self.start_button = QPushButton("Start")
        self.stop_button = QPushButton("Stop")
        self.set_zero_button = QPushButton("Set Zero")

        self.start_button.clicked.connect(self.start_polling)
        self.stop_button.clicked.connect(self.stop_polling)
        self.set_zero_button.clicked.connect(self.set_zero)

        button_layout = QHBoxLayout()
        button_layout.addWidget(self.start_button)
        button_layout.addWidget(self.stop_button)
        button_layout.addWidget(self.set_zero_button)

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.gauge)
        layout.addLayout(button_layout)
        self.setLayout(layout)

        self.bus_id = bus_id
        self.bus = None
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.read_angle)

        if not config.DEBUG_MODE:
            try:
                self.bus = SMBus(self.bus_id)
                log_main_console("success", f"AS5048A ready on bus {self.bus_id}")
            except Exception as e:
                log_main_console("error", f"Failed to open I2C bus {self.bus_id}: {e}")
        else:
            log_main_console("info", "DEBUG_MODE: fake polling setup complete")

    def start_polling(self):
        self.timer.start(50)
        log_main_console("info", "Started I2C angle polling")

    def stop_polling(self):
        self.timer.stop()
        log_main_console("info", "Stopped I2C angle polling")

    def set_zero(self):
        raw = self._read_raw_angle()
        if raw is not None:
            self.zero_raw = raw
            log_main_console("info", f"Zero set to raw value: {self.zero_raw}")

    def _read_raw_angle(self):
        if config.DEBUG_MODE:
            import random

            return random.randint(0, 16383)
        try:
            msb = self.bus.read_byte_data(AS5048_ADDR, REG_ANGLE_MSB)
            lsb = self.bus.read_byte_data(AS5048_ADDR, REG_ANGLE_LSB)
            raw = ((msb << 6) | (lsb & 0x3F)) & 0x3FFF
            return raw
        except Exception as e:
            log_main_console("error", f"I2C read failed: {e}")
            return None

    def read_angle(self):
        current_raw = self._read_raw_angle()
        if current_raw is None or self.zero_raw is None:
            return

        # Calculate signed delta
        diff = (current_raw - self.zero_raw + 8192) % 16384 - 8192
        angle_deg = -((diff * 360.0) / 16384.0)

        # Clamp to 0–90° range for display
        angle_clamped = max(0.0, min(90.0, angle_deg))

        log_main_console(
            "info",
            f"Read raw: {current_raw}, Δ: {diff}, Degrees: {angle_deg:.2f}° → Gauge: {angle_clamped:.2f}°",
        )
        self.gauge.set_angle(angle_clamped)

    def closeEvent(self, event):
        self.timer.stop()
        if self.bus:
            self.bus.close()
        super().closeEvent(event)
