# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
GPIO Skill

Control Raspberry Pi GPIO pins.
Requires: RPi.GPIO (pip install RPi.GPIO)
Falls back gracefully if not on a Pi.
"""

import logging

logger = logging.getLogger(__name__)

# Try to import GPIO library
try:
    import RPi.GPIO as GPIO

    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    HAS_GPIO = True
except ImportError:
    HAS_GPIO = False
    logger.debug("RPi.GPIO not available - GPIO skill will simulate")


def gpio_setup(data: dict) -> str:
    """Set up a GPIO pin."""
    pin = data.get("pin")
    if pin is None:
        return "Please provide a pin number."
    pin = int(pin)
    mode = data.get("mode", "output").lower()
    pull = data.get("pull", "none").lower()

    if not HAS_GPIO:
        return f"[Simulated] Set up GPIO {pin} as {mode}"

    try:
        if mode == "output":
            GPIO.setup(pin, GPIO.OUT)
        else:
            pull_mode = GPIO.PUD_OFF
            if pull == "up":
                pull_mode = GPIO.PUD_UP
            elif pull == "down":
                pull_mode = GPIO.PUD_DOWN
            GPIO.setup(pin, GPIO.IN, pull_up_down=pull_mode)

        return f"✓ GPIO {pin} configured as {mode}"
    except Exception as e:
        return f"Error: {e}. Verify GPIO pin number and that RPi.GPIO is installed on Raspberry Pi hardware."


def gpio_write(data: dict) -> str:
    """Write to a GPIO output pin."""
    pin = data.get("pin")
    state = data.get("state")
    if pin is None or state is None:
        return "Please provide pin and state (high/low)."
    pin = int(pin)

    # Handle various state formats
    if isinstance(state, str):
        state = state.lower() in ["high", "1", "true", "on"]

    if not HAS_GPIO:
        return f"[Simulated] GPIO {pin} -> {'HIGH' if state else 'LOW'}"

    try:
        GPIO.output(pin, GPIO.HIGH if state else GPIO.LOW)
        return f"✓ GPIO {pin} set to {'HIGH' if state else 'LOW'}"
    except Exception as e:
        return f"Error: {e}. Verify GPIO pin number and that RPi.GPIO is installed on Raspberry Pi hardware."


def gpio_read(data: dict) -> str:
    """Read from a GPIO input pin."""
    pin = data.get("pin")
    if pin is None:
        return "Please provide a pin number."
    pin = int(pin)

    if not HAS_GPIO:
        return f"[Simulated] GPIO {pin} = HIGH"

    try:
        state = GPIO.input(pin)
        return f"GPIO {pin} = {'HIGH' if state else 'LOW'}"
    except Exception as e:
        return f"Error: {e}. Verify GPIO pin number and that RPi.GPIO is installed on Raspberry Pi hardware."


def gpio_pwm(data: dict) -> str:
    """Set PWM on a pin."""
    pin = data.get("pin")
    if pin is None:
        return "Please provide a pin number."
    pin = int(pin)
    duty = data.get("duty", 50)  # 0-100
    freq = data.get("frequency", 1000)  # Hz

    if not HAS_GPIO:
        return f"[Simulated] GPIO {pin} PWM: {duty}% @ {freq}Hz"

    try:
        # Note: This is simplified - real implementation would track PWM objects
        pwm = GPIO.PWM(pin, freq)
        pwm.start(duty)
        return f"✓ GPIO {pin} PWM: {duty}% @ {freq}Hz"
    except Exception as e:
        return f"Error: {e}. Verify GPIO pin number and that RPi.GPIO is installed on Raspberry Pi hardware."


# Tool definitions for the agent
TOOLS = [
    {
        "name": "gpio_setup",
        "description": (
            "Configure a Raspberry Pi GPIO pin as input or output using BCM numbering. "
            "Use this before reading or writing a pin to set its direction and pull resistor. "
            "Supports optional pull-up/pull-down for input pins. "
            "Falls back to simulated mode if RPi.GPIO is not installed."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "pin": {"type": "integer", "description": "BCM GPIO pin number (e.g., 17, 18, 27)"},
                "mode": {
                    "type": "string",
                    "enum": ["input", "output"],
                    "description": "Pin mode",
                    "default": "output",
                },
                "pull": {
                    "type": "string",
                    "enum": ["none", "up", "down"],
                    "description": "Pull-up/down resistor (for inputs)",
                    "default": "none",
                },
            },
            "required": ["pin"],
        },
        "handler": gpio_setup,
        "category": "gpio",
    },
    {
        "name": "gpio_write",
        "description": (
            "Set a Raspberry Pi GPIO output pin to HIGH or LOW to control LEDs, relays, or other devices. "
            "Use this after configuring the pin as output with gpio_setup. "
            "Accepts state as high/low, on/off, 1/0, or true/false. "
            "Falls back to simulated mode if RPi.GPIO is not installed."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "pin": {"type": "integer", "description": "BCM GPIO pin number"},
                "state": {
                    "type": "string",
                    "description": "Pin state: high/low, on/off, 1/0, true/false",
                },
            },
            "required": ["pin", "state"],
        },
        "handler": gpio_write,
        "category": "gpio",
    },
    {
        "name": "gpio_read",
        "description": (
            "Read the current HIGH/LOW state of a Raspberry Pi GPIO input pin. "
            "Use this after configuring the pin as input with gpio_setup. "
            "Returns the digital state (HIGH or LOW) of the specified BCM pin number. "
            "Falls back to simulated mode if RPi.GPIO is not installed."
        ),
        "input_schema": {
            "type": "object",
            "properties": {"pin": {"type": "integer", "description": "BCM GPIO pin number"}},
            "required": ["pin"],
        },
        "handler": gpio_read,
        "category": "gpio",
    },
    {
        "name": "gpio_pwm",
        "description": (
            "Set PWM (pulse-width modulation) output on a Raspberry Pi GPIO pin. "
            "Use this for LED dimming, motor speed control, or servo positioning. "
            "Configurable duty cycle (0-100%) and frequency (Hz). "
            "Falls back to simulated mode if RPi.GPIO is not installed."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "pin": {"type": "integer", "description": "BCM GPIO pin number"},
                "duty": {"type": "number", "description": "Duty cycle 0-100%", "default": 50},
                "frequency": {
                    "type": "number",
                    "description": "PWM frequency in Hz",
                    "default": 1000,
                },
            },
            "required": ["pin"],
        },
        "handler": gpio_pwm,
        "category": "gpio",
    },
]
