"""@package docstring
B-ASIC Operation Module.
TODO: More info.
"""

from abc import abstractmethod
from numbers import Number
from typing import List, Dict, Optional, Any, TYPE_CHECKING

from b_asic.graph_component import GraphComponent

if TYPE_CHECKING:
    from b_asic.port import InputPort, OutputPort
    from b_asic.simulation import SimulationState

class Operation(GraphComponent):
    """Operation interface.
    TODO: More info.
    """

    @abstractmethod
    def inputs(self) -> "List[InputPort]":
        """Get a list of all input ports."""
        raise NotImplementedError

    @abstractmethod
    def outputs(self) -> "List[OutputPort]":
        """Get a list of all output ports."""
        raise NotImplementedError

    @abstractmethod
    def input_count(self) -> int:
        """Get the number of input ports."""
        raise NotImplementedError

    @abstractmethod
    def output_count(self) -> int:
        """Get the number of output ports."""
        raise NotImplementedError

    @abstractmethod
    def input(self, i: int) -> "InputPort":
        """Get the input port at index i."""
        raise NotImplementedError


    @abstractmethod
    def output(self, i: int) -> "OutputPort":
        """Get the output port at index i."""
        raise NotImplementedError


    @abstractmethod
    def params(self) -> Dict[str, Optional[Any]]:
        """Get a dictionary of all parameter values."""
        raise NotImplementedError

    @abstractmethod
    def param(self, name: str) -> Optional[Any]:
        """Get the value of a parameter.
        Returns None if the parameter is not defined.
        """
        raise NotImplementedError

    @abstractmethod
    def set_param(self, name: str, value: Any) -> None:
        """Set the value of a parameter.
        The parameter must be defined.
        """
        raise NotImplementedError

    @abstractmethod
    def evaluate_outputs(self, state: "SimulationState") -> List[Number]:
        """Simulate the circuit until its iteration count matches that of the simulation state,
        then return the resulting output vector.
        """
        raise NotImplementedError

    @abstractmethod
    def split(self) -> "List[Operation]":
        """Split the operation into multiple operations.
        If splitting is not possible, this may return a list containing only the operation itself.
        """
        raise NotImplementedError

    @property
    @abstractmethod
    def neighbours(self) -> "List[Operation]":
        """Return all operations that are connected by signals to this operation.
        If no neighbours are found this returns an empty list
        """
        raise NotImplementedError