import time
from logging import Logger
from typing import Optional, Union

from matterlab_serial_device import SerialDevice, open_close
from matterlab_rotavaps.base_rotavap import Rotavap


class IKARV10(Rotavap, SerialDevice):
    category = "Rotavap"
    min_rpm = 0

    _name = "IN_NAME"
    _software = "IN_SOFTWARE"
    
    _reset = "RESET"

    _rpm_query = "IN_PV_4"
    _target_rpm_query = "IN_SP_4"
    
    _rpm_set = "OUT_SP_4"
    _interval_set = "OUT_SP_60"
    _timer_set = "OUT_SP_61"
    _rotate_start = "START_4"
    _rotate_stop = "STOP_4"

    _move_up = "OUT_SP_62 1"
    _move_down = "START_62"
    _move_stop = "STOP_62"

    def __init__(
        self,
        com_port: str,
        max_rpm: int = 200,
        connect_hardware: bool = True,
        encoding: str = "utf-8",
        baudrate: int = 9600,
        timeout: float = 1.0,
        parity: str = "even",
        bytesize: int = 7,
        stopbits: int = 1,
        read_delay: float = 0.5,
        write_delay: float = 0.5,
        logger: Optional[Logger] = None,
        **kwargs,
    ) -> None:
        SerialDevice.__init__(
            self,
            com_port=com_port,
            encoding=encoding,
            baudrate=baudrate,
            timeout=timeout,
            parity=parity,
            bytesize=bytesize,
            stopbits=stopbits,
            connect_hardware=connect_hardware,
            **kwargs,
        )
        Rotavap.__init__(self, max_rpm=max_rpm, logger=logger)
        self._read_delay = read_delay
        self._write_delay = write_delay
        self._rotate = False
        self._interval = 0
        self._timer = 0

        if connect_hardware:
            self.logger.info("Initializing IKA RV10 rotavap on %s", self.com_port)
            self.reset()
        
    @open_close
    def _query_rotavap(self, command: str) -> str:
        response = self.query(write_command=f"{command}\r\n", read_delay=self._read_delay).split()[1]
        self.logger.debug("Query to IKA RV10 on %s: %s -> %s", self.com_port, command, response)
        return response
    
    @property
    def name(self) -> str:
        name = self._query_rotavap(self._name)
        self.logger.info("IKA RV10 on %s reports name %s", self.com_port, name)
        return name
    
    @property
    def software_version(self) -> str:
        software_version = self._query_rotavap(self._software)
        self.logger.info("IKA RV10 on %s software version is %s", self.com_port, software_version)
        return software_version
    
    def reset(self) -> None:
        self.logger.warning("Resetting IKA RV10 on %s", self.com_port)
        self._query_rotavap(self._reset)

    @property
    def rpm(self) -> int:
        """
        current speed in rpm
        
        :param self: Description
        :return: Description
        :rtype: int
        """
        rpm = int(self._query_rotavap(self._rpm_query))
        self.logger.info("Current rotation speed on %s is %s rpm", self.com_port, rpm)
        return rpm
    
    @property
    def target_rpm(self) -> int:
        """
        target speed in rpm
        
        :param self: Description
        :return: Description
        :rtype: int
        """
        target_rpm = int(self._query_rotavap(self._target_rpm_query))
        self.logger.info("Target rotation speed on %s is %s rpm", self.com_port, target_rpm)
        return target_rpm
    
    @rpm.setter
    def rpm(self, rpm:int) -> None:
        if not isinstance(rpm, int):
            raise TypeError("rpm must be an integer")

        if not self._switch_rpm <= rpm <= self.max_rpm:
            raise ValueError(f"RPM out of range: min {self._switch_rpm}, max {self.max_rpm}!")

        self._target_rpm = rpm
        self.logger.info("Setting target rotation speed on %s to %s rpm", self.com_port, rpm)
        self._query_rotavap(f"{self._rpm_set} {rpm}")

        if rpm == self._switch_rpm:
            self._stir_switch = False
        else:
            self._stir_switch = True
    
    @property
    def _stir_switch(self) ->bool:
        return self._stir_switch_status
    
    @_stir_switch.setter
    def _stir_switch(self, stir_switch_status: bool) ->None:
        self._stir_switch_status = stir_switch_status
        if self._stir_switch_status:
            self._query_rotavap(self._rotate_start)
            self.logger.info("Started rotavap rotation on %s", self.com_port)
        else:
            self._query_rotavap(self._rotate_stop)
            self.logger.info("Stopped rotavap rotation on %s", self.com_port)

    @property
    def interval(self) -> int:
        """
        interval in sec
        
        :param self: Description
        :return: Description
        :rtype: int
        """
        return self._interval
    
    @interval.setter
    def interval(self, interval: int) -> None:
        if not isinstance(interval, int):
            raise TypeError("interval must be an interger")
        self._interval = interval
        self._query_rotavap(f"{self._interval_set} {interval}")
        self.logger.info("Set IKA RV10 on %s interval to %s s", self.com_port, interval)

    @property
    def timer(self) -> int:
        """
        timer/min
        
        :param self: Description
        :return: Description
        :rtype: int
        """
        return self._timer
    
    @timer.setter
    def timer(self, timer: int) ->None :
        """
        timer/min
        
        :param self: Description
        :param timer: Description
        :type timer: int
        """
        if not isinstance(timer, int):
            raise TypeError("timer must be an interger")
        self._timer = timer
        self._query_rotavap(f"{self._timer_set} {timer}")
        self.logger.info("Set IKA RV10 on %s timer to %s min", self.com_port, timer)

    def move_up(self) -> None:
        """
        move to top or until move_stop
        
        :param self: Description
        """
        self.logger.info("Moving IKA RV10 on %s up", self.com_port)
        self._query_rotavap(self._move_up)

    def move_down(self) -> None:
        """
        move to bottom or until move_stop
        
        :param self: Description
        """
        self.logger.info("Moving IKA RV10 on %s down", self.com_port)
        self._query_rotavap(self._move_down)

    def move_stop(self) -> None:
        """
        Stop moving
        
        :param self: Description
        """
        self._query_rotavap(self._move_stop)
    def standby(self) -> None:
        self.logger.info("Setting IKA RV10 on %s to standby", self.com_port)
        self.rpm = self._switch_rpm
