"""
B-ASIC Operation Module.
TODO: More info.
"""

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

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

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

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

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

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

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

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

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

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

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

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

	@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.
		"""
		pass

	@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.
		"""
		pass

	@abstractmethod
	def get_op_name(self) -> "GraphIDType":
		"""Returns a string representing the operation name of the operation."""
		pass

	@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
		"""

	# TODO: More stuff.