from math import *
from pyglet.gl import *
from random import random, uniform

TWOPI = pi * 2

__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'sphere', 'colourball', 'torus', 'belt',
           'flare']


def compile(pointer, *args, **kwargs):
    display = glGenLists(1)
    glNewList(display, GL_COMPILE)
    pointer(*args, **kwargs)
    glEndList()
    return display


def ortho(width, height):
    glDisable(GL_LIGHTING)
    glDisable(GL_DEPTH_TEST)
    glMatrixMode(GL_PROJECTION)
    glPushMatrix()
    glLoadIdentity()
    glOrtho(0, width, 0, height, -1, 1)
    glMatrixMode(GL_MODELVIEW)
    glPushMatrix()
    glLoadIdentity()


def frustrum():
    glMatrixMode(GL_PROJECTION)
    glPopMatrix()
    glMatrixMode(GL_MODELVIEW)
    glPopMatrix()
    glEnable(GL_LIGHTING)
    glEnable(GL_DEPTH_TEST)


def crosshair(size, (cx, cy)):
    glBegin(GL_LINES)
    glVertex2f(cx - size, cy)
    glVertex2f(cx + size, cy)
    glVertex2f(cx, cy - size)
    glVertex2f(cx, cy + size)
    glEnd()


def circle(r, seg, (cx, cy)):
    glBegin(GL_LINE_LOOP)
    for i in xrange(seg):
        theta = TWOPI * i / seg
        glVertex2f(cx + cos(theta) * r, cy + sin(theta) * r)
    glEnd()


def disk(rinner, router, segs, tex):
    glEnable(GL_TEXTURE_2D)
    glDisable(GL_LIGHTING)
    glBindTexture(GL_TEXTURE_2D, tex)
    res = segs * 5

    glBegin(GL_TRIANGLE_STRIP)
    texture = 0
    factor = TWOPI / res
    theta = 0
    for n in xrange(res + 1):
        theta += factor
        x = cos(theta)
        y = sin(theta)
        glTexCoord2f(0, texture)
        glVertex2f(rinner * x, rinner * y)
        glTexCoord2f(1, texture)
        glVertex2f(router * x, router * y)
        texture ^= 1
    glEnd()
    glEnable(GL_LIGHTING)
    glDisable(GL_TEXTURE_2D)


def flare(rinner, router, res, prob, tex):
    glEnable(GL_TEXTURE_2D)
    glDisable(GL_LIGHTING)
    glBindTexture(GL_TEXTURE_2D, tex)
    last_x = 1
    last_y = 0
    last_theta = 0
    factor = TWOPI / res
    rdelta = (router - rinner)
    glBegin(GL_QUADS)
    for i in xrange(res + 1):
        theta = last_theta + factor
        x = cos(theta)
        y = sin(theta)
        if random() > prob:
            distance = rinner + rdelta * random()
            avg_theta = (last_theta + theta) / 2
            x0, y0 = rinner * last_x, rinner * last_y
            x1, y1 = rinner * x, rinner * y
            x2, y2 = distance * cos(avg_theta), distance * sin(avg_theta)
            glTexCoord2f(0, 0)
            glVertex2f(x0, y0)
            glTexCoord2f(0, 1)
            glVertex2f(x1, y1)
            glTexCoord2f(1, 0)
            glVertex2f(x2, y2)
            glTexCoord2f(1, 1)
            glVertex2f(x2, y2)
        last_theta = theta
        last_x = x
        last_y = y
    glEnd()
    glEnable(GL_LIGHTING)
    glDisable(GL_TEXTURE_2D)


def sphere(r, lats, longs, tex, lighting=True, fv4=GLfloat * 4):
    """
        Sphere function from the OpenGL red book.
    """
    sphere = gluNewQuadric()
    gluQuadricDrawStyle(sphere, GLU_FILL)
    gluQuadricTexture(sphere, True)
    if lighting:
        gluQuadricNormals(sphere, GLU_SMOOTH)

    glEnable(GL_TEXTURE_2D)
    if lighting:
        glDisable(GL_BLEND)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
    else:
        glDisable(GL_LIGHTING)
    glBindTexture(GL_TEXTURE_2D, tex)

    gluSphere(sphere, r, lats, longs)

    glBindTexture(GL_TEXTURE_2D, 0)
    glDisable(GL_TEXTURE_2D)
    glEnable(GL_LIGHTING)
    glEnable(GL_BLEND)
    gluDeleteQuadric(sphere)


def colourball(r, lats, longs, colour, fv4=GLfloat * 4):
    """
        Sphere function from the OpenGL red book.
    """
    sphere = gluNewQuadric()

    glDisable(GL_BLEND)
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(*colour))
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 1))
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)

    gluSphere(sphere, r, lats, longs)

    glEnable(GL_BLEND)
    gluDeleteQuadric(sphere)


def belt(radius, cross, object, count):
    for i in xrange(count):
        theta = TWOPI * random()
        x, y, z = cos(theta) * radius, 0, sin(theta) * radius
        # Pretend to move horizontally around the x-axis by delta
        # then I multiply it by a rotation matrix about angle theta
        # the z-axis need not to be multiplied by the matrix, as it's 0
        # Do note the rotation is counter clockwise
        delta = cross * uniform(-1, 1)
        x += cos(theta) * delta
        z += sin(theta) * delta
        y += cross * uniform(-1, 1)

        glPushMatrix()
        glTranslatef(x, y, z)
        glCallList(object)
        glPopMatrix()


try:
    from _glgeom import torus
except ImportError:
    def torus(major_radius, minor_radius, n_major, n_minor, material, shininess=125, fv4=GLfloat * 4):
        """
            Torus function from the OpenGL red book.
        """
        glPushAttrib(GL_CURRENT_BIT)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(*material))
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 1))
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess)

        major_s = TWOPI / n_major
        minor_s = TWOPI / n_minor

        def n(x, y, z):
            m = 1.0 / sqrt(x * x + y * y + z * z)
            return x * m, y * m, z * m

        for i in xrange(n_major):
            a0 = i * major_s
            a1 = a0 + major_s
            x0 = cos(a0)
            y0 = sin(a0)
            x1 = cos(a1)
            y1 = sin(a1)

            glBegin(GL_TRIANGLE_STRIP)

            for j in xrange(n_minor + 1):
                b = j * minor_s
                c = cos(b)
                r = minor_radius * c + major_radius
                z = minor_radius * sin(b)

                glNormal3f(*n(x0 * c, y0 * c, z / minor_radius))
                glVertex3f(x0 * r, y0 * r, z)

                glNormal3f(*n(x1 * c, y1 * c, z / minor_radius))
                glVertex3f(x1 * r, y1 * r, z)

            glEnd()
        glPopAttrib()