from typing import Union, overload, Literal
from .NanotecC5E11TCP import NanotecC5E11TCP
from .NanotecC5E11UDP import NanotecC5E11UDP


@overload
def create_controller(protocol: Literal["tcp"], *, deviceIpAddress: str) -> NanotecC5E11TCP: ...


@overload
def create_controller(
    protocol: Literal["udp"],
    *,
    deviceIpAddress: str,
    originatorUdpPort: int = 2222,
    lockUdpPort: bool = True,
    inputId: int = 0x68,
    inputSize: int = 8,
    inputRPI: int = 10000,
    outputId: int = 0x69,
    outputSize: int = 8,
    outputRPI: int = 10000,
) -> NanotecC5E11UDP: ...
def create_controller(protocol: str, deviceIpAddress: str, **kwargs) -> Union[NanotecC5E11TCP, NanotecC5E11UDP]:
    """
    Factory function to create a Nanotec C5-E-11 controller instance based on the selected protocol.

    Args:
        protocol (str): The communication protocol to use. Must be either "udp" or "tcp".
        deviceIpAddress (str): IP address of the motor controller.
        **kwargs: Additional keyword arguments depending on the protocol:
            - If protocol == "tcp":
                port (str): Port for the TCP connectio, default is 44818.
            - If protocol == "udp":
                originatorUdpPort (int): UDP port to use. Default is 2222.
                lockUdpPort (bool): Whether to lock the UDP port. Default is True.
                inputId (int): Input assembly ID. Default is 0x68.
                inputSize (int): Input assembly size in bytes. Default is 8.
                inputRPI (int): Input RPI in microseconds. Default is 10000.
                outputId (int): Output assembly ID. Default is 0x69.
                outputSize (int): Output assembly size in bytes. Default is 8.
                outputRPI (int): Output RPI in microseconds. Default is 10000.

    Returns:
        Union[nanotec_tcp_controller,nanotec_udp_controller]: A controller instance configured for the selected protocol.

    Raises:
        ValueError: If an unsupported protocol is specified.

    Notes:
        - Automatically chooses and initializes the correct controller class.
        - Additional kwargs are passed directly to the constructor.
    """
    if protocol.lower() == "udp":
        return NanotecC5E11UDP(deviceIpAddress=deviceIpAddress, **kwargs)
    elif protocol.lower() == "tcp":
        return NanotecC5E11TCP(deviceIpAddress=deviceIpAddress, **kwargs)
    else:
        raise ValueError(f"Unsupported protocol: {protocol}")
