# Experimental facilities for programmatically creating and
# compositing shaders

# Currently broken

class ShaderProgram(object):
    def __init__(self, varying={}, uniform={}, frag="", vert=""):
        self.varying, self.uniform, self.frag, self.vert = (varying, uniform, frag, vert)

    def get_varying(self):
        return "\n".join([
                "varying %s %s;" % (typ, ",".join(varlist))
                for (typ, varlist) in self.varying.items()])

    def get_uniform(self):
        return "\n".join([
                "uniform %s %s;" % (typ, ",".join(varlist))
                for (typ, varlist) in self.uniform.items()])

    def get_fragshader_fn(self, name="get_fragment_color"):
        return "vec4 %s(){\n%s\n}" % (name, self.frag)

    def get_frag(self):
        return "%s\n%s\n%s\nvoid main() {\n%s\n}" % (self.get_varying(), self.get_uniform(), self.get_fragshader_fn("get_fragment_color"), "gl_FragColor = get_fragment_color();")

    def get_vert(self):
        return "%s\n%s\n\nvoid main() {\n%s\n}" % (self.get_varying(), self.get_uniform(), self.vert)

class MultiShaderProgram(ShaderProgram):
    """creates a new shader by additively composing several"""
    def __init__(self, shaderprograms):
        self.progs = shaderprograms

        self.varying = {}
        for p in self.progs:
            for (k, v) in p.varying.items():
                if self.varying.has_key(k):
                    self.varying[k].extend(v)
                else:
                    self.varying[k] = v

        self.uniform = {}
        for p in self.progs:
            for (k, v) in p.uniform.items():
                if self.uniform.has_key(k):
                    self.uniform[k].extend(v)
                else:
                    self.uniform[k] = v

        self.vert = "\n".join(X.vert for X in self.progs)

        print self.varying
        print self.uniform
        print self.vert

    def mix_fn(self):
        return "return %s;" % (" + ".join([X+"()" for X in self.names]))

    def get_fragshader_fn(self, name="asp_frag_color"):
        acc = ""
        self.names = []
        for i, p in enumerate(self.progs):
            tname = "%s_%d" % (name, i)
            self.names.append(tname)
            acc += p.get_fragshader_fn(tname) + '\n'
        acc += 'vec4 %s() {\n%s\n}' % (name, self.mix_fn())
        return acc

class AdditiveShaderProgram(MultiShaderProgram):
    pass

class MultiplyShaderProgram(MultiShaderProgram):
    def mix_fn(self):
        return "return %s;" % (" * ".join([X+"()" for X in self.names]))

class SubtractShaderProgram(MultiShaderProgram):
    def mix_fn(self):
        return "return %s() - (%s);" % (self.names[0], " + ".join([X+"()" for X in self.names[1:]]))

if __name__=='__main__':
    varying = {'vec4': ['vertex_color']}
    vert = """gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vertex_color = gl_Color;"""

    frag = """return vertex_color ;"""

    p = ShaderProgram(varying=varying, vert=vert, frag=frag)
    sp = AdditiveShaderProgram([p,p,p,p,p])

    import glitch
    from glitch.limbo.objects import ColorfulTriangle
    shadernode = glitch.Shader(children=[ColorfulTriangle()])
    shadernode.vertex = sp.get_vert()
    shadernode.fragment = sp.get_frag()
    import glitch.gtk
    camera = glitch.gtk.GtkCamera(
        children=[shadernode])
    camera.run()
