import abc
import gdb
from common.config import Reg
ABC = abc.ABCMeta('ABC', (object,), {})


class Rtos(ABC):
    @abc.abstractmethod
    def __init__(self, long_type):
        self._stacking = None
        self._saved_regs = None
        self._long = long_type

    @abc.abstractmethod
    def detect():
        """
        Must be static
        :return: true if the Rtos is detected.
        """
        pass

    @abc.abstractmethod
    def name():
        """
        Must be static
        """
        pass

    @abc.abstractmethod
    def get_threads(self):
        """
        :return: list of current threads.
        """
        pass

    @abc.abstractmethod
    def get_queues(self):
        """
        :return: list of queues.
        """
        pass

    @abc.abstractmethod
    def get_timers(self):
        """
        :return: list of timers.
        """
        pass

    @abc.abstractmethod
    def get_heap_info(self):
        """
        :return: heap info.
        """
        pass

    @abc.abstractmethod
    def get_config(self):
        pass

    @abc.abstractmethod
    def get_current_thread(self):
        pass

    @abc.abstractmethod
    def get_thread_registers(self):
        pass

    @abc.abstractmethod
    def get_thread_stacking(self, stack_ptr):
        pass

    def save_regs(self):
        if self._saved_regs is None:
            gdb.execute("flushregs")
            self._saved_regs = []
            for r in self.get_thread_registers():
                self._saved_regs.append(Reg(r.name, gdb.parse_and_eval("$" + r.name).cast(self._long), r.length))

    def switch_thread_back(self):
        if self._saved_regs is not None:
            gdb.execute("flushregs")
            for r in self._saved_regs:
                gdb.execute("set $" + r.name + " = " + str(r.value))
            self._saved_regs = None

    def switch_thread(self, stack_ptr):
        if self._saved_regs is not None:
            print("call to switch_thread_back inside switch_tread")
            self.switch_thread_back()
        self.save_regs()
        self._stacking = self.get_thread_stacking(stack_ptr)
        for reg_name, reg_offset in self._stacking.register_offsets.items():
            if reg_name != 'sp':
                gdb.execute("set $" + reg_name + "=*(int *)(" + str(stack_ptr + reg_offset) + ")")
            else:
                gdb.execute("set $sp=" + str(stack_ptr + self._stacking.stack_size))
