from yta_editor_nodes.processor.base import _ProcessorGPUAndCPU
from yta_editor_parameters.specifications import ParameterSpecification
from typing import Union
from abc import abstractmethod


class _Blender(_ProcessorGPUAndCPU):
    """
    *For internal use only*

    This class must be inherited by the specific
    implementation of some blenders that will use
    CPU or GPU (at least one of the options).

    Class that is capable of mixing 2 different
    inputs by using the GPU or the CPU.

    Mandatory inputs:
    - `base_input`
    - `overlay_input`

    Optional inputs:
    - `None`

    Parameters:
    - `*mix_weight`

    The parameters above are the accepted by this
    node, where `*` means that the parameter is
    mandatory.
    """

    mandatory_inputs = ['base_input', 'overlay_input']
    optional_inputs = []
    parameters_specifications = [
        ParameterSpecification(
            name = 'mix_weight',
            type = float,
            is_required = True,
            default = 1.0,
            min = 0.01,
            max = 1.0
        )
    ]

    def __init__(
        self,
        opengl_context: Union['moderngl.Context', None] = None,
        **kwargs
    ):
        """
        The `do_use_gpu` boolean flag will be set by the
        user when instantiating the class to choose between
        GPU and CPU to process the input.
        """
        blender_cpu, blender_gpu = self._instantiate_cpu_and_gpu_blenders(
            opengl_context = opengl_context,
            **kwargs
        )

        super().__init__(
            processor_cpu = blender_cpu,
            processor_gpu = blender_gpu,
            opengl_context = opengl_context
        )

    @abstractmethod
    def _instantiate_cpu_and_gpu_blenders(
        self,
        opengl_context: Union['moderngl.Context', None],
        **kwargs
    ):
        """
        *For internal use only*

        Instantiate the CPU and GPU blenders and return 
        them in that order.

        This method must be implemented by each class.
        """
        pass

    """
    I maintain this code below because of the way
    we are handling the optional 'base_input'. I'm
    trying to refactor and simplify everything, but
    I'll keep this code until I confirm this node
    is working perfectly after the refactor.
    """
    # def process(
    #     self,
    #     inputs: dict[str, Union['np.ndarray', 'moderngl.Texture']],
    #     # TODO: Maybe set it automatically based on the texture
    #     # size (?)
    #     evaluation_context: EvaluationContext,
    #     output_size: Union[tuple[int, int], None] = None,
    #     do_use_gpu: bool = True,
    #     mix_weight: float = 1.0,
    #     **kwargs
    # # TODO: What about the output type (?)
    # ) -> Union['np.ndarray', 'moderngl.Texture']:
    #     """
    #     Process the provided 'base_input' and 'overlay_input'
    #     with GPU or CPU according to the internal flag.

    #     The `mix_weight` will determine how much of the result
    #     we keep against the base, where a `mix_weight == 1.0`
    #     means that we want the result at 100%, but a
    #     `mix_weight == 0.4` means a 40% of the result and a 60%
    #     of the base.
    #     """
    #     self._validate_inputs(inputs)

    #     base_input = inputs['base_input']
    #     overlay_input = inputs['overlay_input']

    #     output_size = (
    #         get_input_size(base_input)
    #         if output_size is None else
    #         output_size
    #     )

    #     processor = self._get_processor(
    #         do_use_gpu = do_use_gpu
    #     )

    #     # TODO: Transform the inputs if needed, based on
    #     # the type and the processor we got: numpy to
    #     # texture if GPU, texture to numpy if CPU

    #     return processor.process(
    #         inputs = {
    #             'base_input': self._prepare_input(
    #                 input = base_input,
    #                 do_use_gpu = do_use_gpu
    #             ),
    #             'overlay_input': self._prepare_input(
    #                 input = overlay_input,
    #                 do_use_gpu = do_use_gpu
    #             ),
    #         },
    #         evaluation_context = evaluation_context,
    #         output_size = output_size,
    #         mix_weight = mix_weight,
    #         **kwargs
    #     )