from yta_editor_nodes_cpu.abstract import _NodeCPU
from yta_programming.singleton import SingletonABCMeta
from abc import abstractmethod
from typing import Union

import numpy as np


class _NodeProcessorCPU(
    _NodeCPU
):
    """
    *Abstract class*

    *For internal use only*

    Node class, which is to implement specific
    parameters when instantiated and also a specific
    node processor that will process the `input`.
    """

    mandatory_inputs = []
    optional_inputs = []

    def __init__(
        self,
        node_processor: '_NodeProcessorCoreCPU',
        cpu_render_context: 'CPURenderContext'
    ):
        # TODO: Validate that is '_NodeProcessorCoreCPU' or child
        self.node_processor: '_NodeProcessorCoreCPU' = node_processor
        """
        The singleton instance of the processor that will
        process the input and return the output.
        """
        self.cpu_render_context: 'CPURenderContext' = cpu_render_context
        """
        The render context for this CPU node processor.
        """

    # TODO: Can I refactor somewhow to make it have a
    # common validation (?)
    @abstractmethod
    def process(
        self,
        inputs: dict[str, np.ndarray],
        evaluation_context: 'EvaluationContext',
        # TODO: Should we set the 'factor' here to be able
        # to modify it if it is different than None (?)
        **kwargs
    ) -> np.ndarray:
        """
        Process the `inputs` provided by using the node
        processor instance associated to this node and
        return the output.

        This method must be defined for each class.
        """
        pass
        # ParameterValidator.validate_dict_of('inputs', inputs, np.ndarray)

        # # Validate mandatory and optional inputs
        # self._validate_inputs(inputs)

        # return self.process(
        #     inputs = inputs,
        #     output_size = output_size,
        #     **kwargs
        # )

class _NodeProcessorCoreCPU(metaclass = SingletonABCMeta):
    """
    *Singleton class*

    *For internal use only*

    Class to represent a node processor that uses CPU
    to transform the input. This class will be called
    internally by the specific nodes to process the
    inputs.
    """

    # TODO: Just code and the same attributes that the
    # GPU version also has
    @abstractmethod
    def process(
        self,
        input: np.ndarray,
        output_size: Union[tuple[int, int], None],
        **kwargs
    ) -> np.ndarray:
        """
        Process the provided 'input' and transform it by
        using the code that is defined here.
        """
        # TODO: Specific attributes can be received as
        # **kwargs to modify the specific process
        pass