# -*- coding: utf-8 -*-
# Copyright (c) 2015, Vispy Development Team.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.

"""
Normal components are modular shader components used for retrieving or
generating surface normal vectors.

These components generate a function in the fragment shader that accepts no
arguments and returns a vec4 normal vector. Typically, the normal vector
is computed in the vertex shader and passed by varying to the fragment
shader.
"""

from __future__ import division

from .component import VisualComponent
from ..shaders import Varying
from ... import gloo


class TextureComponent(VisualComponent):
    """
    Component that reads a texture uniform.

    A separate texture coordinate component must be provided.
    If the texture coordinate is outside the edge of the texture, then
    the fragment is discarded.
    """

    SHADERS = dict(
        frag_color="""
            vec4 texture_read() {
                vec2 tex_coord = $texture_coordinate();
                if(tex_coord.x < 0.0 || tex_coord.x > 1.0 ||
                tex_coord.y < 0.0 || tex_coord.y > 1.0) {
                    discard;
                }
                return texture2D($texture, tex_coord.xy);
            }
        """)

    def __init__(self, texture, tex_coord_comp):
        super(TextureComponent, self).__init__()
        self.tex_coord_comp = tex_coord_comp
        self.texture = texture
        self._deps = [tex_coord_comp]

    def activate(self, program, mode):
        # Texture coordinates are generated by a separate component.
        ff = self._funcs['frag_color']
        ff['texture_coordinate'] = self.tex_coord_comp.coord_shader()
        #ff['texture'] = ('uniform', 'sampler2D', self.texture)
        ff['texture'] = self.texture


class VertexTextureCoordinateComponent(VisualComponent):
    """
    Class that reads texture coordinates from a vertex buffer.
    """
    SHADERS = dict(
        vert_post_hook="""
            void texture_coord_support() {
                $tex_local_pos = $local_pos;
            }
        """,
        texture_coord="""
            vec2 vertex_tex_coord() {
                vec4 tex_coord = $map_local_to_tex($tex_local_pos);
                return tex_coord.xy;
            }
        """)

    # exclude texture_coord when auto-attaching shaders because the visual
    # does not have a 'texture_coord' hook; instead this function will be
    # called by another component.
    AUTO_ATTACH = ['vert_post_hook']

    def __init__(self, transform):
        super(VertexTextureCoordinateComponent, self).__init__()
        self.transform = transform
        
        # Create Varying to connect vertex / fragment shaders
        var = Varying('v_tex_local_pos', dtype='vec4')
        self.coord_shader()['tex_local_pos'] = var
        self._funcs['vert_post_hook']['tex_local_pos'] = var

    def coord_shader(self):
        """
        Return the fragment shader function that returns a texture coordinate.
        """
        return self._funcs['texture_coord']

    def activate(self, program, mode):
        ff = self.coord_shader()
        ff['map_local_to_tex'] = self.transform
        self._funcs['vert_post_hook']['local_pos'] = \
            self.visual._program.vert['local_pos']


class TextureCoordinateComponent(VisualComponent):
    """
    Component that outputs texture coordinates derived from the local vertex
    coordinate and a transform.
    """

    SHADERS = dict(
        vert_post_hook="""
            void texture_coord_support() {
                $tex_coord_output = $tex_coord;
            }
        """,
        texture_coord="""
            vec2 tex_coord() {
                return $tex_coord_input;
            }
        """)

    # exclude texture_coord when auto-attaching shaders because the visual
    # does not have a 'texture_coord' hook; instead this function will be
    # called by another component.
    AUTO_ATTACH = ['vert_post_hook']

    def __init__(self, coords):
        super(TextureCoordinateComponent, self).__init__()
        self.coords = coords
        self._vbo = None
        
        # Create Varying to connect vertex / fragment shaders
        var = Varying('v_tex_coord', dtype='vec2')
        self.coord_shader()['tex_coord_input'] = var
        self._funcs['vert_post_hook']['tex_coord_output'] = var

    def coord_shader(self):
        """
        Return the fragment shader function that returns a texture coordinate.
        """
        return self._funcs['texture_coord']

    @property
    def vbo(self):
        if self._vbo is None:
            self._vbo = gloo.VertexBuffer(self.coords)
        return self._vbo

    def activate(self, program, mode):
        vf = self._funcs['vert_post_hook']
        #vf['tex_coord_output'] = Varying('v_tex_coord', dtype='vec2')
        #self._funcs['texture_coord']['tex_coord_input'] = \
        #    vf['tex_coord_output']
        vf['tex_coord'] = self.vbo  # attribute vec2
