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

from numbers import Number
from typing import Optional, Sequence

from b_asic.operation import AbstractOperation, ResultKey, RegisterMap, MutableResultMap, MutableRegisterMap
from b_asic.graph_component import Name, TypeName
from b_asic.port import SignalSourceProvider


class Input(AbstractOperation):
    """Input operation.
    TODO: More info.
    """

    def __init__(self, name: Name = ""):
        super().__init__(input_count = 0, output_count = 1, name = name)
        self.set_param("value", 0)

    @property
    def type_name(self) -> TypeName:
        return "in"
    
    def evaluate(self):
        return self.param("value")

    @property
    def value(self) -> Number:
        """Get the current value of this input."""
        return self.param("value")

    @value.setter
    def value(self, value: Number) -> None:
        """Set the current value of this input."""
        self.set_param("value", value)


class Output(AbstractOperation):
    """Output operation.
    TODO: More info.
    """

    def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
        super().__init__(input_count = 1, output_count = 0, name = name, input_sources = [src0])

    @property
    def type_name(self) -> TypeName:
        return "out"

    def evaluate(self, _):
        return None


class Register(AbstractOperation):
    """Unit delay operation.
    TODO: More info.
    """

    def __init__(self, src0: Optional[SignalSourceProvider] = None, initial_value: Number = 0, name: Name = ""):
        super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
        self.set_param("initial_value", initial_value)

    @property
    def type_name(self) -> TypeName:
        return "reg"

    def evaluate(self, a):
        return self.param("initial_value")

    def current_output(self, index: int, registers: Optional[RegisterMap] = None, prefix: str = "") -> Optional[Number]:
        if registers is not None:
            return registers.get(self.key(index, prefix), self.param("initial_value"))
        return self.param("initial_value")
    
    def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Number:
        if index != 0:
            raise IndexError(f"Output index out of range (expected 0-0, got {index})")
        if len(input_values) != 1:
            raise ValueError(f"Wrong number of inputs supplied to SFG for evaluation (expected 1, got {len(input_values)})")
        
        key = self.key(index, prefix)
        value = self.param("initial_value")
        if registers is not None:
            value = registers.get(key, value)
            registers[key] = self.truncate_inputs(input_values)[0]
        if results is not None:
            results[key] = value
        return value