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

from numbers import Number

from b_asic.port import InputPort, OutputPort
from b_asic.operation import Operation
from b_asic.abstract_operation import AbstractOperation
from b_asic.abstract_graph_component import AbstractGraphComponent
from b_asic.graph_component import Name, TypeName


class Input(Operation, AbstractGraphComponent):
    """Input operation.
    TODO: More info.
    """

    # TODO: Implement all functions.

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


class Constant(AbstractOperation):
    """Constant value operation.
    TODO: More info.
    """

    def __init__(self, value: Number = 0, name: Name = ""):
        super().__init__(name)

        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
        self._parameters["value"] = value

    def evaluate(self, inputs: list) -> list:
        return [self.param("value")]

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


class Addition(AbstractOperation):
    """Binary addition operation.
    TODO: More info.
    """

    def __init__(self, source1: OutputPort = None, source2: OutputPort = None, name: Name = ""):
        super().__init__(name)

        self._input_ports = [InputPort(0, self), InputPort(1, self)] # TODO: Generate appropriate ID for ports.
        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.

        if source1 is not None:
            self._input_ports[0].connect_to_port(source1)
        if source2 is not None:
            self._input_ports[1].connect_to_port(source2)

    def evaluate(self, inputs: list) -> list:
        return [inputs[0] + inputs[1]]

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


class ConstantMultiplication(AbstractOperation):
    """Unary constant multiplication operation.
    TODO: More info.
    """

    def __init__(self, coefficient: Number, source1: OutputPort = None, name: Name = ""):
        super().__init__(name)
        self._input_ports = [InputPort(0, self)] # TODO: Generate appropriate ID for ports.
        self._output_ports = [OutputPort(0, self)] # TODO: Generate appropriate ID for ports.
        self._parameters["coefficient"] = coefficient

        if source1 is not None:
            self._input_ports[0].connect_to_port(source1)

    def evaluate(self, inputs: list) -> list:
        return [inputs[0] * self.param("coefficient")]

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