from yta_validation.parameter import ParameterValidator


class RenderResources:
    """
    The resources handler of the rendering process.
    Here you have a list with the funcionalities you
    can interact with:
    - Acquiring and releasing render targets by using
    the `.render_target` attribute.
    """

    def __init__(
        self,
        opengl_context: 'moderngl.Context',
        render_target_provider: 'RenderTargetProvider',
        texture_provider: 'TexturesProvider'
    ):
        ParameterValidator.validate_mandatory_instance_of('opengl_context', opengl_context, 'Context')
        ParameterValidator.validate_mandatory_instance_of('render_target_provider', render_target_provider, 'RenderTargetProvider')
        ParameterValidator.validate_mandatory_instance_of('texture_provider', texture_provider, 'TextureProvider')

        self.opengl_context: 'moderngl.Context' = opengl_context
        """
        The moderngl context we need to operate with GPU.
        """
        self._render_target_provider: 'RenderTargetProvider' = render_target_provider
        """
        *For internal use only*

        The render target provider, able to handle the
        pool and acquire and release `RenderTarget`
        instances on demand.
        """
        self._texture_provider: 'TextureProvider' = texture_provider
        """
        *For internal use only*

        The texture provider, able to handle the pool and
        acquire and release `moderngl.Texture` instances
        on demand.
        """

        """
        TODO: Maybe having a pool for the textures, that
        is used by the RenderTarget provider is also an
        interesting thing...
        """

        """
        Exposed API below
        """
        self.render_target: '_RenderTargetAccessor' = _RenderTargetAccessor(self._render_target_provider)
        """
        The functionality related to the `RenderTarget`
        instances in one click, by using an instance of its
        provider class.
        """
        self.texture: '_TextureAccessor' = _TextureAccessor(self._texture_provider)
        """
        The functionality related to the `moderngl.Texture`
        pool handling in one click, by using an instance of
        the texture provider class.
        """
        

"""
API exposer wrappers below
"""
class _RenderTargetAccessor:
    """
    *For internal use only*

    Wrapper to simplify the access to the functionality
    that the `RenderTargetProvider` is providing to us
    and limitate the part of its API we expose.
    """

    def __init__(
        self,
        render_target_provider: 'RenderTargetProvider'
    ):
        self._render_target_provider: 'RenderTargetProvider' = render_target_provider
        """
        *For internal use only*

        The `RenderTargetProvider` instance that allows us
        to have the functionality.
        """

    def acquire(
        self,
        width: int = 1920,
        height: int = 1080,
        number_of_components: int = 4,
        dtype: str = 'f1'
    ) -> 'RenderTarget':
        """
        Get a `RenderTarget` instance with the given `width`,
        `height`, `number_of_components` and `dtype` from the
        pool, that will mark it as in use.
        """
        return self._render_target_provider.acquire(
            width = width,
            height = height,
            number_of_components = number_of_components,
            dtype = dtype
        )
    
    def acquire_like(
        self,
        render_target: 'RenderTarget'
    ) -> 'RenderTarget':
        """
        Get the `RenderTarget` instance with the same
        properties than the `render_target` provided as
        parameter.
        """
        return self._render_target_provider.acquire_like(
            render_target = render_target
        )

    def release(
        self,
        render_target: 'RenderTarget'
    ) -> None:
        """
        Release the `render_target` provided, making it
        available again through the pool and marking it
        as free.
        """
        self._render_target_provider.release(render_target)

class _TextureAccessor:
    """
    *For internal use only*

    Wrapper to simplify the access to the functionality
    that the `TextureProvider` is providing to us and
    limitate the part of its API we expose.
    """

    def __init__(
        self,
        texture_provider: 'TextureProvider'
    ):
        self._texture_provider: 'TextureProvider' = texture_provider
        """
        *For internal use only*

        The `TextureProvider` instance that allows us to
        have the functionality related to interacting
        with the texture pool.
        """

    def acquire(
        self,
        width: int = 1920,
        height: int = 1080,
        number_of_components: int = 4,
        dtype: str = 'f1'
    ) -> 'moderngl.Texture':
        """
        Get a `moderngl.Texture` instance with the given `width`,
        `height`, `number_of_components` and `dtype` from the
        pool.
        """
        return self._texture_provider.acquire(
            width = width,
            height = height,
            number_of_components = number_of_components,
            dtype = dtype
        )
    
    def acquire_like(
        self,
        texture: 'moderngl.Texture'
    ) -> 'moderngl.Texture':
        """
        Get the `moderngl.Texture` instance with the same
        properties than the `texture` provided as
        parameter.
        """
        return self._texture_provider.acquire_like(
            texture = texture
        )

    def release(
        self,
        texture: 'moderngl.Texture'
    ) -> None:
        """
        Release the `texture` provided, making it
        available again through the pool.
        """
        self._texture_provider.release(texture)