#!/usr/bin/env python3
"""
Comprehensive examples for up-py Python bindings.

This module demonstrates various features of the up-py library,
which provides Python bindings for the up-rust (uProtocol) crate.
"""

import up_py


# =============================================================================
# Example 1: Basic Publisher-Subscriber Pattern
# =============================================================================

def example_basic_pubsub():
    """
    Demonstrates the basic publish-subscribe pattern.
    
    This example shows how to:
    - Create a URI provider with custom authority and entity ID
    - Set up a local transport for communication
    - Register a listener callback
    - Publish a message
    - Receive and process the message
    """
    print("\n=== Example 1: Basic Pub-Sub ===")
    
    # Define the resource ID for the communication channel
    RESOURCE_ID = 0xb4c1
    
    # Callback function that will be invoked when messages arrive
    def message_handler(msg):
        """
        Handle incoming messages.
        
        Args:
            msg (UMessage): The received message object
        """
        content = msg.extract_string()
        if content:
            print(f"📨 Received: {content}")
    
    # Create URI provider
    # Parameters: authority (string), entity_id (u32), version (u8)
    uri_provider = up_py.StaticUriProvider("my-vehicle", 0xa34b, 0x01)
    
    # Create local transport for in-process communication
    transport = up_py.LocalTransport()
    
    # Register the message handler
    transport.register_listener(uri_provider, RESOURCE_ID, message_handler)
    
    # Create a publisher
    publisher = up_py.SimplePublisher(transport, uri_provider)
    
    # Create and publish a message
    payload = up_py.UPayload.from_string("Hello, uProtocol!")
    publisher.publish(RESOURCE_ID, payload)
    
    print("✅ Message published and received successfully")


# =============================================================================
# Example 2: Multiple Messages with Different Payloads
# =============================================================================

def example_multiple_messages():
    """
    Demonstrates publishing multiple messages with different payloads.
    
    Shows how to:
    - Send multiple messages in sequence
    - Use different payload creation methods
    - Handle multiple messages in a single listener
    """
    print("\n=== Example 2: Multiple Messages ===")
    
    RESOURCE_ID = 0x1234
    
    # Counter for tracking received messages
    message_count = [0]  # Using list to allow modification in nested function
    
    def counting_handler(msg):
        """
        Handler that counts and displays messages.
        
        Args:
            msg (UMessage): The received message
        """
        message_count[0] += 1
        content = msg.extract_string()
        if content:
            print(f"📬 Message #{message_count[0]}: {content}")
    
    # Setup
    uri_provider = up_py.StaticUriProvider("test-device", 0x5678, 0x01)
    transport = up_py.LocalTransport()
    transport.register_listener(uri_provider, RESOURCE_ID, counting_handler)
    publisher = up_py.SimplePublisher(transport, uri_provider)
    
    # Publish multiple messages
    messages = [
        "First message",
        "Second message",
        "Third message with more content",
        "Final message 🎉"
    ]
    
    for text in messages:
        payload = up_py.UPayload.from_string(text)
        publisher.publish(RESOURCE_ID, payload)
    
    print(f"✅ Sent and received {message_count[0]} messages")


# =============================================================================
# Example 3: Different Resource IDs (Multiple Channels)
# =============================================================================

def example_multiple_channels():
    """
    Demonstrates using multiple resource IDs (channels).
    
    Shows how to:
    - Register listeners for different resource IDs
    - Route messages to specific channels
    - Use different handlers for different channels
    """
    print("\n=== Example 3: Multiple Channels ===")
    
    # Define different channels
    TELEMETRY_CHANNEL = 0x0001
    COMMAND_CHANNEL = 0x0002
    STATUS_CHANNEL = 0x0003
    
    def telemetry_handler(msg):
        """Handle telemetry data."""
        content = msg.extract_string()
        if content:
            print(f"📊 [TELEMETRY] {content}")
    
    def command_handler(msg):
        """Handle commands."""
        content = msg.extract_string()
        if content:
            print(f"⚙️  [COMMAND] {content}")
    
    def status_handler(msg):
        """Handle status updates."""
        content = msg.extract_string()
        if content:
            print(f"ℹ️  [STATUS] {content}")
    
    # Setup
    uri_provider = up_py.StaticUriProvider("multi-channel-device", 0x9999, 0x01)
    transport = up_py.LocalTransport()
    
    # Register different handlers for different channels
    transport.register_listener(uri_provider, TELEMETRY_CHANNEL, telemetry_handler)
    transport.register_listener(uri_provider, COMMAND_CHANNEL, command_handler)
    transport.register_listener(uri_provider, STATUS_CHANNEL, status_handler)
    
    # Create publisher
    publisher = up_py.SimplePublisher(transport, uri_provider)
    
    # Publish to different channels
    telemetry_payload = up_py.UPayload.from_string("Temperature: 25.5°C")
    publisher.publish(TELEMETRY_CHANNEL, telemetry_payload)
    
    command_payload = up_py.UPayload.from_string("START_ENGINE")
    publisher.publish(COMMAND_CHANNEL, command_payload)
    
    status_payload = up_py.UPayload.from_string("System ready")
    publisher.publish(STATUS_CHANNEL, status_payload)
    
    print("✅ Messages routed to different channels")


# =============================================================================
# Example 4: Using Bytes Payload
# =============================================================================

def example_bytes_payload():
    """
    Demonstrates creating payloads from raw bytes.
    
    Shows how to:
    - Create payloads from byte arrays
    - Work with binary data
    """
    print("\n=== Example 4: Bytes Payload ===")
    
    RESOURCE_ID = 0xabcd
    
    def binary_handler(msg):
        """Handle binary messages."""
        # Note: extract_string works for protobuf string values
        # For raw bytes, you'd need additional handling
        content = msg.extract_string()
        print(f"📦 Received binary message: {content if content else '[binary data]'}")
    
    # Setup
    uri_provider = up_py.StaticUriProvider("binary-device", 0xdead, 0x01)
    transport = up_py.LocalTransport()
    transport.register_listener(uri_provider, RESOURCE_ID, binary_handler)
    publisher = up_py.SimplePublisher(transport, uri_provider)
    
    # Create payload from bytes
    binary_data = b"Hello from bytes!"
    payload = up_py.UPayload.from_bytes(list(binary_data))
    publisher.publish(RESOURCE_ID, payload)
    
    print("✅ Binary payload sent")


# =============================================================================
# Example 5: Publishing Without Payload
# =============================================================================

def example_empty_payload():
    """
    Demonstrates publishing messages without payload.
    
    Shows how to:
    - Send notification-style messages
    - Use None as payload
    """
    print("\n=== Example 5: Empty Payload ===")
    
    RESOURCE_ID = 0xbeef
    
    def notification_handler(msg):
        """Handle notification messages."""
        content = msg.extract_string()
        if content:
            print(f"🔔 Notification received: {content}")
        else:
            print("🔔 Empty notification received")
    
    # Setup
    uri_provider = up_py.StaticUriProvider("notification-device", 0xcafe, 0x01)
    transport = up_py.LocalTransport()
    transport.register_listener(uri_provider, RESOURCE_ID, notification_handler)
    publisher = up_py.SimplePublisher(transport, uri_provider)
    
    # Publish with no payload
    publisher.publish(RESOURCE_ID, None)
    
    # Publish with payload for comparison
    payload = up_py.UPayload.from_string("This has content")
    publisher.publish(RESOURCE_ID, payload)
    
    print("✅ Notifications sent")


# =============================================================================
# Example 6: Building a Simple Service
# =============================================================================

class TemperatureService:
    """
    Example service class that publishes temperature readings.
    
    This demonstrates how to wrap up-py functionality in a class
    for better organization and reusability.
    """
    
    def __init__(self, authority: str, entity_id: int, version: int):
        """
        Initialize the temperature service.
        
        Args:
            authority: The authority name (e.g., "vehicle-123")
            entity_id: The entity ID for this service
            version: The version number
        """
        self.TEMP_RESOURCE_ID = 0x1000
        self.uri_provider = up_py.StaticUriProvider(authority, entity_id, version)
        self.transport = up_py.LocalTransport()
        self.publisher = up_py.SimplePublisher(self.transport, self.uri_provider)
        
    def subscribe(self, callback):
        """
        Subscribe to temperature updates.
        
        Args:
            callback: Function to call when temperature updates arrive
        """
        self.transport.register_listener(
            self.uri_provider, 
            self.TEMP_RESOURCE_ID, 
            callback
        )
    
    def publish_temperature(self, temperature: float):
        """
        Publish a temperature reading.
        
        Args:
            temperature: The temperature value in Celsius
        """
        message = f"Temperature: {temperature}°C"
        payload = up_py.UPayload.from_string(message)
        self.publisher.publish(self.TEMP_RESOURCE_ID, payload)


def example_service_class():
    """
    Demonstrates using a service class wrapper.
    
    Shows how to:
    - Organize up-py code in classes
    - Create reusable service components
    - Encapsulate publish-subscribe logic
    """
    print("\n=== Example 6: Service Class ===")
    
    def temperature_callback(msg):
        """Handle temperature updates."""
        content = msg.extract_string()
        if content:
            print(f"🌡️  {content}")
    
    # Create service instance
    temp_service = TemperatureService("smart-home", 0x1234, 0x01)
    
    # Subscribe to updates
    temp_service.subscribe(temperature_callback)
    
    # Publish some readings
    temp_service.publish_temperature(22.5)
    temp_service.publish_temperature(23.1)
    temp_service.publish_temperature(24.0)
    
    print("✅ Service class example completed")


# =============================================================================
# Main Entry Point
# =============================================================================

def main():
    """
    Run all examples.
    
    This demonstrates various use cases and patterns for using
    the up-py Python bindings for uProtocol.
    """
    print("=" * 60)
    print("up-py Python Bindings - Comprehensive Examples")
    print("=" * 60)
    
    try:
        example_basic_pubsub()
        example_multiple_messages()
        example_multiple_channels()
        example_bytes_payload()
        example_empty_payload()
        example_service_class()
        
        print("\n" + "=" * 60)
        print("✅ All examples completed successfully!")
        print("=" * 60)
        
    except Exception as e:
        print(f"\n❌ Error occurred: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    main()
