"""
driver
TB6612FNG モータードライバを制御するモジュール
差動2輪駆動で左右のモーターを制御する
"""

from common import config

try:
    import RPi.GPIO as GPIO
except Exception:
    GPIO = None


class MotorDriver:
    """TB6612FNG を介して左右のモーターを制御するクラス"""

    def __init__(self) -> None:
        self._pwm_a: object = None
        self._pwm_b: object = None
        self._ready: bool = False

    def start(self) -> None:
        """GPIO を初期化して PWM を開始する"""
        if GPIO is None:
            print("GPIO 未検出: モーター無効（非 RPi 環境）")
            return

        GPIO.setmode(GPIO.BOARD)

        # モーター A（左）
        GPIO.setup(config.MA_IN1, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(config.MA_IN2, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(config.MA_PWM, GPIO.OUT, initial=GPIO.LOW)

        # モーター B（右）
        GPIO.setup(config.MB_IN1, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(config.MB_IN2, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(config.MB_PWM, GPIO.OUT, initial=GPIO.LOW)

        self._pwm_a = GPIO.PWM(
            config.MA_PWM, config.MOTOR_PWM_FREQ,
        )
        self._pwm_b = GPIO.PWM(
            config.MB_PWM, config.MOTOR_PWM_FREQ,
        )
        self._pwm_a.start(0)
        self._pwm_b.start(0)
        self._ready = True
        print("モーター初期化完了")

    @staticmethod
    def _clamp(
        value: float, low: float, high: float,
    ) -> float:
        """値を指定範囲に制限する"""
        return max(low, min(high, value))

    def _apply_one_motor(
        self, in1: int, in2: int, pwm: object,
        speed: float,
    ) -> None:
        """1つのモーターに速度を適用する

        Args:
            in1: IN1 の GPIO ピン番号
            in2: IN2 の GPIO ピン番号
            pwm: PWM オブジェクト
            speed: -1.0 ~ +1.0 の速度値
        """
        if speed > 0:
            GPIO.output(in1, GPIO.LOW)
            GPIO.output(in2, GPIO.HIGH)
        elif speed < 0:
            GPIO.output(in1, GPIO.HIGH)
            GPIO.output(in2, GPIO.LOW)
        else:
            GPIO.output(in1, GPIO.LOW)
            GPIO.output(in2, GPIO.LOW)
        pwm.ChangeDutyCycle(abs(speed) * 100.0)

    def set_drive(
        self, throttle: float, steer: float,
    ) -> None:
        """throttle と steer からモーターを駆動する

        Args:
            throttle: 前後方向 (-1.0 ~ +1.0)
            steer: 左右方向 (-1.0 ~ +1.0)
        """
        if not self._ready:
            return

        throttle = self._clamp(throttle, -1.0, 1.0)
        steer = self._clamp(steer, -1.0, 1.0)

        # ステアリング方向の補正
        if config.STEER_REVERSED:
            steer = -steer

        # 差動2輪: 左右の速度を計算
        left = self._clamp(throttle + steer, -1.0, 1.0)
        right = self._clamp(throttle - steer, -1.0, 1.0)

        # モーター配線の極性補正
        if config.MOTOR_LEFT_REVERSED:
            left = -left
        if config.MOTOR_RIGHT_REVERSED:
            right = -right

        self._apply_one_motor(
            config.MA_IN1, config.MA_IN2,
            self._pwm_a, left,
        )
        self._apply_one_motor(
            config.MB_IN1, config.MB_IN2,
            self._pwm_b, right,
        )

    def stop(self) -> None:
        """モーターを停止する"""
        self.set_drive(0.0, 0.0)

    def cleanup(self) -> None:
        """GPIO リソースを解放する"""
        if not self._ready:
            return
        self.stop()
        self._pwm_a.stop()
        self._pwm_b.stop()
        GPIO.cleanup()
        self._ready = False
