# SPDX-License-Identifier: GPL-3.0-only
# (C) Copyright CERN 2021. All rights not expressly granted are reserved. 

from enum import Enum
from typing import List, Set, Tuple

from availsim4core.src.context.context import Context
from availsim4core.src.discrete_event_simulation.event.c_event.c_event import CEvent
from availsim4core.src.discrete_event_simulation.event.event import Event
from availsim4core.src.timeline.record import Record


class BEventPriority(Enum):
    """
    class used to easily order the B Events when they are occuring exactly at the same timestamp
    """

    BLIND_FAILURE_EVENT = -2
    DETECTABLE_FAILURE_EVENT = -1

    JUMP_PHASE_EVENT = 0
    NEXT_PHASE_IF_SPECIFIC_FAILURE_EVENT = 1
    NEXT_PHASE_IF_FAILURE_EVENT = 2
    NEXT_PHASE_EVENT = 3

    END_HOLDING_EVENT = 4

    END_REPAIRING_EVENT = 5
    MRU_END_REPAIRING_EVENT = 6

    START_REPAIRING_EVENT = 7
    MRU_START_REPAIRING_EVENT = 8

    END_INSPECTION_EVENT = 9
    START_INSPECTION_EVENT = 10

    def __lt__(self, other):
        if self.__class__ is other.__class__:
            return self.value < other.value
        return NotImplemented


class BEvent(Event):
    """
    B  events  =  Bound  events,  also  called  Timed  events,  Unconditioned  events.
    B event only occurs on the Basic components.
    Those events are planned, do not need any conditions to be fulfilled in order to happen but just occur due to a delay being expired.
    In our case, it would be a failure occurring or a repair finishing (and its propagation in the system), a periodic inspection starting, the end of a phase.
    """
    __slots__ = 'absolute_occurrence_time', 'priority'

    def __init__(self,
                 absolute_occurrence_time: float,
                 context: Context,
                 priority: BEventPriority):
        super().__init__(context)
        self.absolute_occurrence_time = absolute_occurrence_time
        self.priority = priority

    def __eq__(self, other):
        return self.absolute_occurrence_time == other.absolute_occurrence_time and \
               self.priority == other.priority

    def __lt__(self, other):
        return self.absolute_occurrence_time < other.absolute_occurrence_time or \
               (self.absolute_occurrence_time == other.absolute_occurrence_time and self.priority < other.priority)

    def is_context_valid(self):
        return True

    def postpone(self, duration):
        """
        This function is only implemented for blind failure event and detectable failure event.
        :param duration: ttf := ttf + duration
        :return: event
        """
        return self

    def execute(self) -> List[Record]:
        """
        update the status of a component by calling update_status
        the new status depend on the type of B event
        """
        return []

    def generate_c_event(self, **kwargs) -> Set[CEvent]:
        """
        Returns the set of CEvents generated by this particular b_event.
        """
        return set()

    def update_b_event_collection(self,
                                event_set: Set,
                                types_of_event_to_clean: List[type]) -> Tuple[Set, Set]:
        """
        The input event_set is cleaned from all the events of types provided by types_of_event_to_clean.
        Returns a clean set of Events and a set containing the removed events.
        """
        event_deleted: Set = set()
        return event_set, event_deleted

    def update_c_event_collection(self,
                                event_set: Set,
                                types_of_event_to_clean: List[type]) -> Tuple[Set, Set]:
        """
        The input event_set is cleaned from all the events of types provided by types_of_event_to_clean.
        Returns a clean set of Events and a set containing the removed events.
        """
        event_deleted: Set = set()
        return event_set, event_deleted

    @staticmethod
    def _b_events_to_be_cleaned() -> List[Event]:
        """
        Type list of Events that will be affected by this particular event.
        """
        return []

    @staticmethod
    def _c_events_to_be_cleaned() -> List[Event]:
        """
        Type list of Events that will be affected by this particular event.
        """
        return []