"""
blocks.py — Lectura del sensor de color TCS34725 i servo d'expulsió de blocs.
Equivalent a blocks.cpp del codi Arduino/ESP32.

Requereix /boot/config.txt:
    dtoverlay=i2c-gpio,bus=4,i2c_gpio_sda=22,i2c_gpio_scl=27
"""

import time
import pigpio
import adafruit_extended_bus
import adafruit_tcs34725

from pins import SERVO_PWM

# ==================
# IDs de color
# ==================

BK = 0   # Negre (no reconegut)
RD = 1   # Vermell  → avançar
GN = 2   # Verd     → girar dreta
BU = 3   # Blau     → girar esquerra
YE = 4   # Groc     → xuclar líquid
OG = 5   # Taronja  → buidar líquid
VT = 6   # Violeta  → sorpresa

NUM_COLORS = 7

# Taula de colors de referència (valors RGB 0–255, calibrats amb el sensor)
_COLORS = [
    {"name": "BK", "r":  80, "g":  80, "b":  80},
    {"name": "RD", "r": 202, "g":  32, "b":  34},
    {"name": "GN", "r": 107, "g":  90, "b":  57},
    {"name": "BU", "r": 104, "g":  83, "b":  66},
    {"name": "YE", "r": 150, "g":  69, "b":  33},
    {"name": "OG", "r": 185, "g":  44, "b":  32},
    {"name": "VT", "r": 129, "g":  70, "b":  55},
]

# ==================
# Paràmetres del servo
# ==================

# Valors en µs de pulse width (pigpio set_servo_pulsewidth).
# Conversió des de l'ESP32 (16 bits, 50Hz): valor/65535 * 20000µs
# MIN  = 3277/65535 * 20000 ≈ 1000µs
# MAX  = 8000/65535 * 20000 ≈ 2440µs
# EJECT= 6450/65535 * 20000 ≈ 1968µs
MIN_SERVO_US   = 1000   # µs → ~0°
MAX_SERVO_US   = 2440   # µs → ~180°
OPEN_POSITION  = 2440   # µs (equivalent a 8000 ESP32)
EJECT_POSITION = 1968   # µs (equivalent a 6450 ESP32)
_INCREMENT_US  = 3      # µs per iteració de 1ms (equivalent a increment de 10 ESP32)

# ==================
# Instàncies globals
# ==================

_pi: pigpio.pi           = None
_color_sensor            = None
_current_servo_pos: int  = OPEN_POSITION


# ==================
# Setup
# ==================

def blocks_setup(pi: pigpio.pi):
    """
    Inicialitza el servo i el sensor de color TCS34725.
    El servo arranca en OPEN_POSITION.
    """
    global _pi, _color_sensor, _current_servo_pos

    _pi = pi

    # Servo
    pi.set_mode(SERVO_PWM, pigpio.OUTPUT)
    _current_servo_pos = OPEN_POSITION
    pi.set_servo_pulsewidth(SERVO_PWM, _current_servo_pos)

    # Sensor de color TCS34725 via bus I2C 4 (bit-bang GPIO22=SDA, GPIO27=SCL)
    i2c = adafruit_extended_bus.ExtendedI2C(4)
    _color_sensor = adafruit_tcs34725.TCS34725(i2c)
    _color_sensor.integration_time = 50    # ms
    _color_sensor.gain = 4                 # 4x (equivalent a TCS34725::Gain::X04)


# ==================
# Servo
# ==================

def servo_move_to(target_us: int):
    """
    Mou el servo fins a target_us de forma suau.
    Incrementa/decrementa _INCREMENT_US cada mil·lisegon.
    """
    global _current_servo_pos

    if not (MIN_SERVO_US <= target_us <= MAX_SERVO_US):
        return

    while True:
        if _current_servo_pos < target_us - _INCREMENT_US:
            _current_servo_pos += _INCREMENT_US
        elif _current_servo_pos > target_us + _INCREMENT_US:
            _current_servo_pos -= _INCREMENT_US
        else:
            _current_servo_pos = target_us

        _pi.set_servo_pulsewidth(SERVO_PWM, _current_servo_pos)
        time.sleep(0.001)

        if _current_servo_pos == target_us:
            return


# ==================
# Sensor de color
# ==================

def _calc_colors_difference(measured: tuple, reference: dict) -> int:
    """Distància Manhattan entre el color mesurat i un color de referència."""
    return (abs(measured[0] - reference["r"]) +
            abs(measured[1] - reference["g"]) +
            abs(measured[2] - reference["b"]))


def read_color_raw() -> tuple:
    """Retorna (r, g, b) en bytes 0-255 sense classificació. Útil per calibrar."""
    try:
        return _color_sensor.color_rgb_bytes
    except Exception:
        return (0, 0, 0)


def read_block_color() -> int:
    """
    Llegeix el color del bloc des del sensor TCS34725.
    Compara contra la taula de referència per distància Manhattan.
    Retorna l'ID del color més proper, o BK si cap supera el llindar.
    """
    MAX_DIFFERENCE = 15

    try:
        r, g, b = _color_sensor.color_rgb_bytes
    except Exception:
        return BK

    min_difference   = MAX_DIFFERENCE
    min_diff_color   = BK

    for color_id, ref in enumerate(_COLORS):
        diff = _calc_colors_difference((r, g, b), ref)
        if diff < min_difference:
            min_difference = diff
            min_diff_color = color_id

    return min_diff_color
