import logging

import moderngl
from moderngl_window.loaders.base import BaseLoader
from moderngl_window.opengl import program
from moderngl_window.exceptions import ImproperlyConfigured

logger = logging.getLogger(__name__)


class Loader(BaseLoader):
    kind = "single"

    def load(self) -> moderngl.Program:
        """Loads a shader program from a single glsl file.

        Each shader type is separated by preprocessors

        - VERTEX_SHADER
        - FRAGMENT_SHADER
        - GEOMETRY_SHADER
        - TESS_CONTROL_SHADER
        - TESS_EVALUATION_SHADER

        Example:

        .. code:: glsl

            #version 330

            #if defined VERTEX_SHADER

            in vec3 in_position;
            in vec2 in_texcoord_0;
            out vec2 uv0;

            void main() {
                gl_Position = vec4(in_position, 1);
                uv0 = in_texcoord_0;
            }

            #elif defined FRAGMENT_SHADER

            out vec4 fragColor;
            uniform sampler2D texture0;
            in vec2 uv0;

            void main() {
                fragColor = texture(texture0, uv0);
            }
            #endif

        Returns:
            moderngl.Program: The Program instance
        """
        self.meta.resolved_path, source = self._load_source(self.meta.path)
        shaders = program.ProgramShaders.from_single(self.meta, source)
        shaders.handle_includes(self._load_source)
        prog = shaders.create()

        # Wrap the program if reloadable is set
        if self.meta.reloadable:
            # Disable reload flag so reloads will return Program instances
            self.meta.reloadable = False
            # Wrap it ..
            prog = program.ReloadableProgram(self.meta, prog)

        return prog

    def _load_source(self, path):
        """Finds and loads a single source file.

        Args:
            path: Path to resource
        Returns:
            Tuple[resolved_path, source]: The resolved path and the source
        """
        resolved_path = self.find_program(path)
        if not resolved_path:
            raise ImproperlyConfigured("Cannot find program '{}'".format(path))

        logger.info("Loading: %s", path)

        with open(str(resolved_path), "r") as fd:
            return resolved_path, fd.read()
