from .GameObjectReference import GameObjectReference
from .MaterialManager import MaterialManager
from .Process import Process

import traceback
import sys
import bge

__all__ = ['ShaderMaterial']

class ShaderMaterial(Process):
    class InvalidContext(RuntimeError): pass

    MaterialManager = MaterialManager
    FragmentShader = ''
    VertexShader = ''

    @property
    def owner(self):
        return self.reference.object

    @property
    def invalid(self):
        return self.owner is None

    def __init__(self, *args, **kargs):
        self.MaterialManager = kargs.pop('manager', self.MaterialManager)
        self.FragmentShader = kargs.pop('fragment', self.FragmentShader)
        self.VertexShader = kargs.pop('vertex', self.VertexShader)

        # Fetch the object at initialisation
        object = kargs.pop('object', None)
        if object is None:
            try:
                controller = bge.logic.getCurrentController()
                object = controller.owner
            except SystemError:
                self.owner = None
                self.error(
                    self.InvalidContext('No KX_GameObject could be fetched'))
                return

        self.reference = GameObjectReference(object)
        self.init(*args, **kargs)
        self.MaterialManager.Register(self)

    def filterShader(self, object, mesh, shader):
        '''Filter function: how to discriminate the shaders'''
        return shader is not None

    def getShaders(self):
        '''Should return the shaders to keep track of'''
        for mesh in self.owner.meshes:
            for material in mesh.materials:
                shader = material.getShader()
                if self.filterShader(object, mesh, shader):
                    if not shader.isValid():
                        shader.setSource(
                            self.VertexShader,
                            self.FragmentShader,
                            True)
                    yield shader

    def init(self):
        '''This is called on shader instanciation'''

    def error(self, error):
        '''This is called on error (invalidation)'''
        sys.stderr.write('Error occured in object "{}":\n'.format(self.owner))
        traceback.print_exc()

    def pre_draw_setup_dispatch(self):
        for shader in self.getShaders():
            self.pre_draw_setup(shader)

    def pre_draw_dispatch(self):
        for shader in self.getShaders():
            self.pre_draw(shader)

    def post_draw_dispatch(self):
        for shader in self.getShaders():
            self.post_draw(shader)
