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

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from availsim4core.src.context.system.component_tree.component import Component

from availsim4core.src.context.context import Context
from availsim4core.src.discrete_event_simulation.event.b_event.failure_event.failure_event_factory import \
    FailureEventFactory
from availsim4core.src.discrete_event_simulation.event.b_event.inspection_event.inspection_event_factory import \
    InspectionEventFactory
from availsim4core.src.discrete_event_simulation.event.b_event.phase_event.next_phase_event import NextPhaseEvent


class BEventGenerator:
    """
    This static class provides methods to initialise the BEvents of the simulation.
    """

    @classmethod
    def generate_first_events(cls,
                              absolute_simulation_time: float,
                              context: Context):
        """
        Function generating the first B events when a simulation is started.
        Those events are generated by the first failures of components, periodic inspections, first change of phases etc.
        """
        set_of_b_events = set()
        set_of_b_events.update(
            cls.get_b_events_inspection(
                absolute_simulation_time,
                context,
                context.root_component
            )
        )
        set_of_b_events.update(
            cls._generate_first_phase_events(
                absolute_simulation_time, context
            )
        )
        return set_of_b_events

    @staticmethod
    def _generate_first_phase_events(absolute_simulation_time: float,
                                     context: Context):
        """
        Function generating the first B events related to phase changes
        """
        next_phase_time = absolute_simulation_time + context.phase_manager.current_phase.law.get_random_value()
        event = NextPhaseEvent(next_phase_time,
                               context)
        return {event}

    @classmethod
    def get_b_events_inspection(cls,
                                absolute_simulation_time: float,
                                context: Context,
                                component: Component):
        """
        Function generating first inspection B events of a component recursively,
        calling itself on children of the current component if any
        """
        set_of_events = set()
        for child_component in component.get_children():
            events = cls.get_b_events_inspection(absolute_simulation_time,
                                                 context,
                                                 child_component)
            set_of_events.update(events)
        if not component.get_children():
            inspection_event = InspectionEventFactory.build(absolute_simulation_time,
                                                            context,
                                                            component,
                                                            component.failure_mode)
            if inspection_event is not None:
                set_of_events.add(inspection_event)

        return set_of_events
