"""
This Python library provides functionalities to control an FM transmitter.
"""

import xmlrpc.client
from robot.api.deco import keyword
from typing import Optional, Dict

class FMTransmitter:
    """
    A Robot Framework library for controlling an FM
    transmitter running on a remote device via XML-RPC.

    == Configuration ==

    The transmitter is configured via a dictionary (for example, loaded from a
    YAML file and passed into the `FM Transmitter init` keyword). The
    configuration must include an address and port for the remote XML-RPC server

    === Configuration Sample (config.yaml) ===

    | FMTransmitterConfig:
    |   address: 172.16.1.189
    |   port: 5000

    = General Usage =

    Setup and initialize FM transmitter from a YAML configuration. Initiate connection with XML-RPC Server running on RPi and set default
    configurations. Then, `FM Transmitter Set Frequency` tunes to the given frequency transmit frequency.

    == Default Settings Applied on Initialization ==

    When `FM Transmitter init` is run successfully, the library applies these
    defaults on the remote device:

    - mute: False
    - mono: True
    - bass_level: 2  (allowed: 0..3)
    - RF power level: 15 (allowed: 0..15)
    - PGA gain: 12 (allowed: 9..12)

    = Example ==

    | ***** Settings *****
    | Library    parrot.FMTransmitter
    | Variables  config.yaml
    |
    | ***** Test Cases *****
    | Run FM Transmitter
    |     FM Transmitter Init        ${FMTransmitterConfig}   # Initiate FM Transmitter
    |     FM Transmitter Set Frequency    95.5                # Sets the Transmit frequency to 95.5.

    """

    ROBOT_LIBRARY_SCOPE = 'GLOBAL'

    def __init__(self) -> None:
        self.xml_rpc_client: Optional[xmlrpc.client.ServerProxy] = None

    @keyword("FM Transmitter init")
    def initialize_transmitter(self,config: Dict[str,str])->bool:
        """
        Initialize the FM transmitter with default values.

        default values:
            - mute: False
            - mono: True
            - bass_level: 2  (allowed: 0..3)
            - RF power level: 15 (allowed: 0..15)
            - PGA gain: 12 (allowed: 9..12)

        *Syntax*

        | `FM Transmitter Init` | <CONFIG> |

        *Examples*

        | `FM Transmitter Init` | ${FMTransmitterConfig} | # Initialize FM tranmitter from the config YAML file. |

        """
        try:
            self.xml_rpc_client = xmlrpc.client.ServerProxy(f"http://{config['address']}:{config['port']}")
            self._check_device_connection()

            # Apply default settings
            self._set_mute(False)
            self._mono_control(True)
            self._bass_control(2)
            self._set_power_level(15)
            self._set_pga_gain(12)

            print("FM Transmitter initialized with default values.")
            return True

        except xmlrpc.client.Fault as e:
            raise RuntimeError(f"FM Transmitter initialization failed due to XML-RPC error: {e}")

        except Exception as e:
             raise RuntimeError(f"FM Transmitter initialization failed: {e}")


    @keyword("FM Transmitter Set Frequency")
    def fm_transmitter_set_frequency(self, frequency: float) -> bool:
        """
        Sets the FM transmission frequency.

        *Syntax*

        | `FM Transmitter Set Frequency` | <Frequency> |


        *Example*

        | `FM Transmitter Set Frequency` |  89.5 | # Sets the transmit frequency to 89.5 |
        """
        try:
            result: bool = self.xml_rpc_client.set_frequency(frequency)
            return result
        except xmlrpc.client.Fault as e:
            raise RuntimeError(f"Setting FM frequency failed due to XML-RPC error: {e}")

        except Exception as e:
            raise RuntimeError(f"Setting FM frequency failed: {e}")

    # -------------------------
    # Internal methods
    # -------------------------

    def _check_device_connection(self) -> bool:
        try:
            if not self.xml_rpc_client.check_device_connection():
                raise RuntimeError(f"Device connection failed")
        except Exception as e:
            raise RuntimeError(f"Device connection check failed: {e}")


    def _set_mute(self, mute: bool) -> bool:
        try:
            if not self.xml_rpc_client.set_mute(mute):
                raise RuntimeError("Set mute failed")
        except Exception as e:
            raise RuntimeError(f"Set mute failed: {e}")

    def _mono_control(self, mono: bool) -> bool:
        try:
            if not self.xml_rpc_client.set_mono_control(mono):
                raise RuntimeError("Set mono failed")
        except Exception as e:
            raise RuntimeError(f"Set mono/stereo failed: {e}")

    def _bass_control(self, bass_level: int) -> bool:
        try:
            if bass_level not in range(4):
                raise ValueError("bass_level must be 0 to 3")
            if not self.xml_rpc_client.set_bass_control(bass_level):
                raise RuntimeError("Set bass failed")
        except Exception as e:
            raise RuntimeError(f"Set bass failed: {e}")

    def _set_power_level(self, RF_level: int) -> bool:
        try:
            if RF_level not in range(16):
                raise ValueError("RF_level must be 0 to 15")
            if not self.xml_rpc_client.set_power_level(RF_level):
                raise RuntimeError("Set power level failed")
        except Exception as e:
            raise RuntimeError(f"Set power level failed: {e}")

    def _set_pga_gain(self, gain_level: int) -> bool:
        try:
            if gain_level not in range(9, 13):
                raise ValueError("PGA gain must be 9 to 12")
            if not  self.xml_rpc_client.set_pga_gain(gain_level):
                raise RuntimeError("Set pga gain failed")
        except Exception as e:
            raise RuntimeError(f"Set pga gain failed: {e}")

