import time
from abc import ABC, abstractmethod


class NanotecBase(ABC):
    def __init__(self, deviceIpAddress: str):
        self.toggle = False
        self.timerDuration = 5
        self.failureTimer = time.perf_counter()
        self.deviceIpAddress = deviceIpAddress
        self._pdiCmd = {
            "Switch Off": 1,
            "Clear Error": 2,
            "Quick Stop": 3,
            "OD-Read": 14,
            "OD-Write": 15,
            "Auto Setup": 16,
            "Home-Current_Pos": 17,
            "Homing": 18,
            "ProfilePosAbs": 20,
            "ProfilePosRel": 21,
            "ProfileVelocity": 23,
            "ProfileTorque": 25,
        }
        self._pdiStatusMapping = {
            "PdiStatusOperationEnabled": 0,
            "PdiStatusWarning": 1,
            "PdiStatusFault": 2,
            "PdiStatusTargetReached": 3,
            "PdiStatusFollowingError": 4,
            "PdiStatusLimitReached": 5,
            "PdiStatusQsHaltBit": 7,
            "PdiStatusHomingDone": 3,
            "PdiStatusAutosetupDone": 4,
            "PdiStatusToggleCmd": 6,
            "PdiStatusError": 7,
        }

    ##########################
    ## Public Class Methods ##
    ##########################

    ####################
    ## Public Getters ##
    ####################
    @abstractmethod
    def get_od(self, index: int, subindex: int, **kwargs):
        pass

    @abstractmethod
    def get_pole_pair(self):
        pass

    @abstractmethod
    def get_is_open_loop(self):
        pass

    @abstractmethod
    def get_is_closed_loop(self):
        pass

    @abstractmethod
    def get_current_reduction_settings(self):
        pass

    @abstractmethod
    def get_analog_config(self):
        pass

    @abstractmethod
    def get_analog_1_mode(self):
        pass

    @abstractmethod
    def get_analog_2_mode(self):
        pass

    @abstractmethod
    def get_analog_1_input(self):
        pass

    @abstractmethod
    def get_analog_2_input(self):
        pass

    @abstractmethod
    def get_digital_output(self, outputIndex: int):
        pass

    @abstractmethod
    def get_torque_forming_current(self):
        pass

    @abstractmethod
    def get_max_voltage(self):
        pass

    @abstractmethod
    def get_max_current(self):
        pass

    @abstractmethod
    def get_acceleration(self):
        pass

    @abstractmethod
    def get_deceleration(self):
        pass

    ####################
    ## Public Setters ##
    ####################
    @abstractmethod
    def set_od(self, index: int, subindex: int, value: int, valueType: str = "UNSIGNED32"):
        pass

    @abstractmethod
    def set_move(
        self,
        moveType: str = "relative",
        targetPosition: int = 5000,
        maxSpeed: int = 200,
        bypassError: bool = False,
    ):
        pass

    @abstractmethod
    def set_move_torque_current_avg(
        self,
        moveType: str = "relative",
        targetPosition: int = 5000,
        maxSpeed: int = 200,
        filterBelow: int = None,
        bypassError: bool = False,
    ):
        pass

    @abstractmethod
    def set_failure_timer_duration(self, duration: float):
        pass

    @abstractmethod
    def set_pole_pair(self, stepAngle: float = 1.8):
        pass

    @abstractmethod
    def set_open_loop(self):
        pass

    @abstractmethod
    def set_closed_loop(self):
        pass

    @abstractmethod
    def set_enable_current_reduction(self, idleTimeout: int = 500, currReductionPercent: int = 90):
        pass

    @abstractmethod
    def set_disable_current_reduction(self):
        pass

    @abstractmethod
    def set_auto_setup(self):
        pass

    @abstractmethod
    def set_analog_config(self):
        pass

    @abstractmethod
    def set_analog_1_mode(self, mode: str = "current"):
        pass

    @abstractmethod
    def set_analog_2_mode(self, mode: str = "current"):
        pass

    @abstractmethod
    def set_digital_output(self, outputIndex: int, outputValue: int):
        pass

    @abstractmethod
    def set_max_voltage(self, voltageValue: int = 4000):
        pass

    @abstractmethod
    def set_max_current(self, currentValue: int = 2000):
        pass

    @abstractmethod
    def set_acceleration(self, accelValue: int = 2000):
        pass

    @abstractmethod
    def set_deceleration(self, decelValue: int = 2000):
        pass

    ###########################
    ## Private Class Methods ##
    ###########################

    #####################
    ## Private Getters ##
    #####################
    @abstractmethod
    def _get_auto_setup_complete(self):
        pass

    @abstractmethod
    def _get_target_reached(self):
        pass

    #####################s
    ## Private Setters ##
    #####################

    #####################
    ## Private Helpers ##
    #####################
    @abstractmethod
    def _modify_bit(self, bitIndex: int, value: int, byteToModify: int):
        pass

    @abstractmethod
    def _check_bit(self, decimalValue: int, bitPosition: int, expectedValue: int):
        pass

    @abstractmethod
    def _start_timer(self):
        pass

    @abstractmethod
    def _timer_is_done(self):
        pass

    @abstractmethod
    def _filter_data(self, below: bool = True, threshold: int = None, dataSet: list = []):
        pass
