#!/usr/bin/python
import os
import math
import wx
from wx import glcanvas
import time
import threading

import pyglet
pyglet.options['shadow_window'] = False
pyglet.options['debug_gl'] = False
from pyglet.gl import *

import stltool

import threading


class GLPanel(wx.Panel):
    '''A simple class for using OpenGL with wxPython.'''

    def __init__(self, parent, id, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0):
        # Forcing a no full repaint to stop flickering
        style = style | wx.NO_FULL_REPAINT_ON_RESIZE
        #call super function
        super(GLPanel, self).__init__(parent, id, pos, size, style)

        #init gl canvas data
        self.GLinitialized = False
        attribList = (glcanvas.WX_GL_RGBA,  # RGBA
                      glcanvas.WX_GL_DOUBLEBUFFER,  # Double Buffered
                      glcanvas.WX_GL_DEPTH_SIZE, 24)  # 24 bit
        # Create the canvas
        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.canvas = glcanvas.GLCanvas(self, attribList=attribList)
        self.sizer.Add(self.canvas, 1, wx.EXPAND)
        self.SetSizer(self.sizer)
        #self.sizer.Fit(self)
        self.Layout()

        # bind events
        self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
        self.canvas.Bind(wx.EVT_SIZE, self.processSizeEvent)
        self.canvas.Bind(wx.EVT_PAINT, self.processPaintEvent)

    #==========================================================================
    # Canvas Proxy Methods
    #==========================================================================
    def GetGLExtents(self):
        '''Get the extents of the OpenGL canvas.'''
        return self.canvas.GetClientSize()

    def SwapBuffers(self):
        '''Swap the OpenGL buffers.'''
        self.canvas.SwapBuffers()

    #==========================================================================
    # wxPython Window Handlers
    #==========================================================================
    def processEraseBackgroundEvent(self, event):
        '''Process the erase background event.'''
        pass  # Do nothing, to avoid flashing on MSWin

    def processSizeEvent(self, event):
        '''Process the resize event.'''
        if self.canvas.GetContext():
            # Make sure the frame is shown before calling SetCurrent.
            self.Show()
            self.canvas.SetCurrent()
            size = self.GetGLExtents()
            self.winsize = (size.width, size.height)
            self.width, self.height = size.width, size.height
            self.OnReshape(size.width, size.height)
            self.canvas.Refresh(False)
        event.Skip()

    def processPaintEvent(self, event):
        '''Process the drawing event.'''
        self.canvas.SetCurrent()

        # This is a 'perfect' time to initialize OpenGL ... only if we need to
        if not self.GLinitialized:
            self.OnInitGL()
            self.GLinitialized = True

        self.OnDraw()
        event.Skip()

    def Destroy(self):
        #clean up the pyglet OpenGL context
        #self.pygletcontext.destroy()
        #call the super method
        super(wx.Panel, self).Destroy()

    #==========================================================================
    # GLFrame OpenGL Event Handlers
    #==========================================================================
    def OnInitGL(self):
        '''Initialize OpenGL for use in the window.'''
        #create a pyglet context for this panel
        self.pmat = (GLdouble * 16)()
        self.mvmat = (GLdouble * 16)()
        self.pygletcontext = Context(current_context)
        self.pygletcontext.set_current()
        self.dist = 1000
        self.vpmat = None
        #normal gl init
        glClearColor(0, 0, 0, 1)
        glColor3f(1, 0, 0)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        # Uncomment this line for a wireframe view
        #glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)

        # Simple light setup.  On Windows GL_LIGHT0 is enabled by default,
        # but this is not the case on Linux or Mac, so remember to always
        # include it.
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glEnable(GL_LIGHT1)

        # Define a simple function to create ctypes arrays of floats:
        def vec(*args):
            return (GLfloat * len(args))(*args)

        glLightfv(GL_LIGHT0, GL_POSITION, vec(.5, .5, 1, 0))
        glLightfv(GL_LIGHT0, GL_SPECULAR, vec(.5, .5, 1, 1))
        glLightfv(GL_LIGHT0, GL_DIFFUSE, vec(1, 1, 1, 1))
        glLightfv(GL_LIGHT1, GL_POSITION, vec(1, 0, .5, 0))
        glLightfv(GL_LIGHT1, GL_DIFFUSE, vec(.5, .5, .5, 1))
        glLightfv(GL_LIGHT1, GL_SPECULAR, vec(1, 1, 1, 1))

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.5, 0, 0.3, 1))
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, vec(1, 1, 1, 1))
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50)
        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, vec(0, 0.1, 0, 0.9))
        #create objects to draw
        #self.create_objects()

    def OnReshape(self, width, height):
        '''Reshape the OpenGL viewport based on the dimensions of the window.'''

        if not self.GLinitialized:
            self.OnInitGL()
            self.GLinitialized = True
        self.pmat = (GLdouble * 16)()
        self.mvmat = (GLdouble * 16)()
        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(60., width / float(height), .1, 1000.)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        #pyglet stuff
        self.vpmat = (GLint * 4)(0, 0, *list(self.GetClientSize()))
        glGetDoublev(GL_PROJECTION_MATRIX, self.pmat)
        glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
        #glMatrixMode(GL_PROJECTION)

        # Wrap text to the width of the window
        if self.GLinitialized:
            self.pygletcontext.set_current()
            self.update_object_resize()

    def OnDraw(self, *args, **kwargs):
        """Draw the window."""
        #clear the context
        self.canvas.SetCurrent()
        self.pygletcontext.set_current()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        #draw objects
        self.draw_objects()
        #update screen
        self.SwapBuffers()

    #==========================================================================
    # To be implemented by a sub class
    #==========================================================================
    def create_objects(self):
        '''create opengl objects when opengl is initialized'''
        pass

    def update_object_resize(self):
        '''called when the window recieves only if opengl is initialized'''
        pass

    def draw_objects(self):
        '''called in the middle of ondraw after the buffer has been cleared'''
        pass


def _dist(dist):
    """return axis length, or 0 if None"""
    if dist is None:
        return 0
    else:
        return float(dist)

class gcpoint(object):
    """gcode point
    stub for first line"""
    def __init__(self, x=0,y=0,z=0,e=0):
        self.x = x
        self.y = y
        self.z = z
        self.e = e
        self.length = 0


class gcline(object):
    """gcode move line
    Once initialised,it knows its position, length and extrusion ratio
    Returns lines into gcview batch()
    """
    def __init__(self, x=None, y=None, z=None, e=None, f=None, prev_gcline=None, orgline = False):
        if prev_gcline is None:
            self.prev_gcline = gcpoint()
        else:
            self.prev_gcline = prev_gcline
        if x is None:
            self.x = self.prev_gcline.x
        else:
            self.x = float(x)
        if y is None:
            self.y = self.prev_gcline.y
        else:
            self.y = float(y)
        if z is None:
            self.z = self.prev_gcline.z
        else:
            self.z = float(z)
        if e is None:
            self.e = self.prev_gcline.e
        else:
            self.e = float(e)

        self.f = f

        self.orgline = orgline

        self.calc_delta()
        self.calc_len()

    def __str__(self):
        return u"line from %s,%s,%s to %s,%s,%s with extrusion ratio %s and feedrate %s\n%s" % (
                self.prev_gcline.x,
                self.prev_gcline.y,
                self.prev_gcline.z,
                self.x,
                self.y,
                self.z,
                self.extrusion_ratio,
                self.f,
                self.orgline,
            )

    def calc_delta(self, prev_gcline=None):
        if prev_gcline is None:
            prev_gcline = self.prev_gcline
        if self.prev_gcline is not None:
            self.dx = self.x - prev_gcline.x
            self.dy = self.y - prev_gcline.y
            self.dz = self.z - prev_gcline.z
            self.de = self.e - prev_gcline.e
        else:
            self.dx = self.x
            self.dy = self.y
            self.dz = self.z
            self.de = self.e

    def calc_len(self):
        if self.dz != 0:
            self.length = math.sqrt(self.dx**2 + self.dy**2 + self.dz**2)
        else:
            self.length = math.sqrt(self.dx**2 + self.dy**2)
        if self.de:
            self.extrusion_ratio = self.length / self.de
        else:
            self.extrusion_ratio = 0

    def glline(self):
        return [
                self.prev_gcline.x,
                self.prev_gcline.y,
                self.prev_gcline.z,
                self.x,
                self.y,
                self.z,
            ]
    def glcolor(self, upper_limit = None, lower_limit = 0, max_feedrate = 0):
        if self.extrusion_ratio == 0:
            return [255,255,255,0,0,0]
        else:
            blue_color = 0
            green_color = 0
            if upper_limit is not None:
                if self.extrusion_ratio <= lower_limit:
                    blue_color = 0
                else:
                    blue_color = int ((self.extrusion_ratio - lower_limit) / (upper_limit - lower_limit) * 255)
            else:
                blue_color = 0
            if max_feedrate > 0 and self.f > 0:
                green_color = int((self.f/max_feedrate) * 255)

            if green_color > 255:
                green_color = 255
            if green_color < 0:
                green_color = 0
            if blue_color > 255:
                blue_color = 255
            if blue_color < 0:
                blue_color = 0
            return[255,green_color,blue_color,128,green_color,blue_color/4]


def float_from_line(axe, line):
    return float(line.split(axe)[1].split(" ")[0])

class gcThreadRenderer(threading.Thread):
    def __init__(self, gcview, lines):
        threading.Thread.__init__(self)
        self.gcview = gcview
        self.lines = lines
        print "q init"

    def run(self):
        for line in self.lines:
            layer_name = line.z
            if line.z not in self.gcview.layers:
                self.gcview.layers[line.z] = pyglet.graphics.Batch()
                self.gcview.layerlist = self.gcview.layers.keys()
                self.gcview.layerlist.sort()
            self.gcview.layers[line.z].add(2, GL_LINES, None, ("v3f", line.glline()), ("c3B", line.glcolor(self.gcview.upper_limit, self.gcview.lower_limit, self.gcview.max_feedrate)))
        self.gcview.t2 = time.time()
        print "Rendered lines in %fs" % (self.gcview.t2-self.gcview.t1)

class gcview(object):
    """gcode visualiser
    Holds opengl objects for all layers
    """
    def __init__(self, lines, batch, w=0.5, h=0.5):
        if len(lines) == 0:
            return
        print "Loading %s lines" % (len(lines))
        #End pos of previous mode
        self.prev = gcpoint()
        # Correction for G92 moves
        self.delta = [0, 0, 0, 0]
        self.layers = {}
        self.t0 = time.time()
        self.lastf = 0
        lines = [self.transform(i) for i in lines]
        lines = [i for i in lines if i is not None]
        self.t1 = time.time()
        print "transformed %s lines in %fs" % (len(lines), self.t1- self.t0)
        self.upper_limit = 0
        self.lower_limit = None
        self.max_feedrate = 0
        for line in lines:
            if line.extrusion_ratio and line.length > 0.005:  #lines shorter than 0.003 can have large extrusion ratio
                if line.extrusion_ratio > self.upper_limit:
                    self.upper_limit = line.extrusion_ratio
                if self.lower_limit is None or line.extrusion_ratio < self.lower_limit:
                    self.lower_limit = line.extrusion_ratio
            if line.f > self.max_feedrate:
                self.max_feedrate = line.f
        #print upper_limit, lower_limit
        #self.render_gl(lines)
        q = gcThreadRenderer(self, lines)
        q.setDaemon(True)
        q.start()




    def transform(self, line):
        """transforms line of gcode into gcline object (or None if its not move)
        Tracks coordinates across resets in self.delta
        """
        orgline = line
        line = line.split(";")[0]
        cur = [None, None, None, None, None]
        if len(line) > 0:
            if "G92" in line:
                #Recalculate delta on G92 (reset)
                if("X" in line):
                    try:
                        self.delta[0] = float_from_line("X", line) + self.prev.x
                    except:
                        self.delta[0] = 0
                if("Y" in line):
                    try:
                        self.delta[1] = float_from_line("Y", line) + self.prev.y
                    except:
                        self.delta[1] = 0
                if("Z" in line):
                    try:
                        self.delta[2] = float_from_line("Z", line) + self.prev.z
                    except:
                        self.delta[2] = 0
                if("E" in line):
                    try:
                        self.delta[3] = float_from_line("E", line) + self.prev.e
                    except:
                        self.delta[3] = 0
                return None

            if "G1" in line or "G0" in line:
                #Create new gcline
                if("X" in line):
                    cur[0] = float_from_line("X", line) + self.delta[0]
                if("Y" in line):
                    cur[1] = float_from_line("Y", line) + self.delta[1]
                if("Z" in line):
                    cur[2] = float_from_line("Z", line) + self.delta[2]
                if("E" in line):
                    cur[3] = float_from_line("E", line) + self.delta[3]
                if "F" in line:
                    cur[4] = float_from_line("F", line)


                if cur == [None, None, None, None, None]:
                    return None
                else:
                    #print cur
                    if cur[4] is None:
                        cur[4] = self.lastf
                    else:
                        self.lastf = cur[4]

                    r = gcline(x=cur[0], y=cur[1], z=cur[2],e=cur[3], f=cur[4], prev_gcline=self.prev, orgline=orgline)
                    self.prev = r
                    return r
            return None

    def delete(self):
        #for i in self.vlists:
        #    i.delete()
        #self.vlists = []
        pass


def trackball(p1x, p1y, p2x, p2y, r):
    TRACKBALLSIZE = r

#float a[3]; /* Axis of rotation */
#float phi;  /* how much to rotate about axis */
#float p1[3], p2[3], d[3];
#float t;

    if (p1x == p2x and p1y == p2y):
        return [0.0, 0.0, 0.0, 1.0]

    p1 = [p1x, p1y, project_to_sphere(TRACKBALLSIZE, p1x, p1y)]
    p2 = [p2x, p2y, project_to_sphere(TRACKBALLSIZE, p2x, p2y)]
    a = stltool.cross(p2, p1)

    d = map(lambda x, y: x - y, p1, p2)
    t = math.sqrt(sum(map(lambda x: x * x, d))) / (2.0 * TRACKBALLSIZE)

    if (t > 1.0):
        t = 1.0
    if (t < -1.0):
        t = -1.0
    phi = 2.0 * math.asin(t)

    return axis_to_quat(a, phi)


def vec(*args):
    return (GLfloat * len(args))(*args)


def axis_to_quat(a, phi):
    #print a, phi
    lena = math.sqrt(sum(map(lambda x: x * x, a)))
    q = map(lambda x: x * (1 / lena), a)
    q = map(lambda x: x * math.sin(phi / 2.0), q)
    q.append(math.cos(phi / 2.0))
    return q


def build_rotmatrix(q):
    m = (GLdouble * 16)()
    m[0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2])
    m[1] = 2.0 * (q[0] * q[1] - q[2] * q[3])
    m[2] = 2.0 * (q[2] * q[0] + q[1] * q[3])
    m[3] = 0.0

    m[4] = 2.0 * (q[0] * q[1] + q[2] * q[3])
    m[5] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0])
    m[6] = 2.0 * (q[1] * q[2] - q[0] * q[3])
    m[7] = 0.0

    m[8] = 2.0 * (q[2] * q[0] - q[1] * q[3])
    m[9] = 2.0 * (q[1] * q[2] + q[0] * q[3])
    m[10] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0])
    m[11] = 0.0

    m[12] = 0.0
    m[13] = 0.0
    m[14] = 0.0
    m[15] = 1.0
    return m


def project_to_sphere(r, x, y):
    d = math.sqrt(x * x + y * y)
    if (d < r * 0.70710678118654752440):
        return math.sqrt(r * r - d * d)
    else:
        t = r / 1.41421356237309504880
        return t * t / d


def mulquat(q1, rq):
    return [q1[3] * rq[0] + q1[0] * rq[3] + q1[1] * rq[2] - q1[2] * rq[1],
                    q1[3] * rq[1] + q1[1] * rq[3] + q1[2] * rq[0] - q1[0] * rq[2],
                    q1[3] * rq[2] + q1[2] * rq[3] + q1[0] * rq[1] - q1[1] * rq[0],
                    q1[3] * rq[3] - q1[0] * rq[0] - q1[1] * rq[1] - q1[2] * rq[2]]


class TestGlPanel(GLPanel):

    def __init__(self, parent, size, id=wx.ID_ANY):
        super(TestGlPanel, self).__init__(parent, id, wx.DefaultPosition, size, 0)
        self.batches = []
        self.rot = 0
        self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.move)
        self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.double)
        self.initialized = 1
        self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.wheel)
        self.parent = parent
        self.initpos = None
        self.dist = 200
        self.bedsize = [200, 200]
        self.transv = [0, 0, -self.dist]
        self.basequat = [0, 0, 0, 1]
        wx.CallAfter(self.forceresize)
        self.mousepos = [0, 0]

    def double(self, event):
        p = event.GetPositionTuple()
        sz = self.GetClientSize()
        v = map(lambda m, w, b: b * m / w, p, sz, self.bedsize)
        v[1] = self.bedsize[1] - v[1]
        v += [300]
        print v
        self.add_file("../prusa/metric-prusa/x-end-idler.stl", v)

    def forceresize(self):
        self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] + 1))
        self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] - 1))
        threading.Thread(target=self.update).start()
        self.initialized = 0

    def move_shape(self, delta):
        """moves shape (selected in l, which is list ListBox of shapes)
        by an offset specified in tuple delta.
        Positive numbers move to (rigt, down)"""
        name = self.parent.l.GetSelection()
        if name == wx.NOT_FOUND:
            return False

        name = self.parent.l.GetString(name)

        model = self.parent.models[name]
        model.offsets = [
                model.offsets[0] + delta[0],
                model.offsets[1] + delta[1],
                model.offsets[2]
            ]
        self.Refresh()
        return True

    def move(self, event):
        """react to mouse actions:
        no mouse: show red mousedrop
        LMB: move active object,
            with shift rotate viewport
        RMB: nothing
            with shift move viewport
        """
        if event.Dragging() and event.LeftIsDown():
            if self.initpos == None:
                self.initpos = event.GetPositionTuple()
            else:
                if not event.ShiftDown():
                    currentpos = event.GetPositionTuple()
                    delta = (
                            (currentpos[0] - self.initpos[0]),
                            -(currentpos[1] - self.initpos[1])
                        )
                    self.move_shape(delta)
                    self.initpos = None
                    return
                #print self.initpos
                p1 = self.initpos
                self.initpos = None
                p2 = event.GetPositionTuple()
                sz = self.GetClientSize()
                p1x = (float(p1[0]) - sz[0] / 2) / (sz[0] / 2)
                p1y = -(float(p1[1]) - sz[1] / 2) / (sz[1] / 2)
                p2x = (float(p2[0]) - sz[0] / 2) / (sz[0] / 2)
                p2y = -(float(p2[1]) - sz[1] / 2) / (sz[1] / 2)
                #print p1x,p1y,p2x,p2y
                quat = trackball(p1x, p1y, p2x, p2y, -self.transv[2] / 250.0)
                if self.rot:
                    self.basequat = mulquat(self.basequat, quat)
                #else:
                glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
                #self.basequat = quatx
                mat = build_rotmatrix(self.basequat)
                glLoadIdentity()
                glTranslatef(self.transv[0], self.transv[1], 0)
                glTranslatef(0, 0, self.transv[2])
                glMultMatrixd(mat)
                glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
                self.rot = 1

        elif event.ButtonUp(wx.MOUSE_BTN_LEFT):
            if self.initpos is not None:
                self.initpos = None
        elif event.ButtonUp(wx.MOUSE_BTN_RIGHT):
            if self.initpos is not None:
                self.initpos = None

        elif event.Dragging() and event.RightIsDown() and event.ShiftDown():
            if self.initpos is None:
                self.initpos = event.GetPositionTuple()
            else:
                p1 = self.initpos
                p2 = event.GetPositionTuple()
                sz = self.GetClientSize()
                p1 = list(p1)
                p2 = list(p2)
                p1[1] *= -1
                p2[1] *= -1

                self.transv = map(lambda x, y, z, c: c - self.dist * (x - y) / z,  list(p1) + [0],  list(p2) + [0],  list(sz) + [1],  self.transv)

                glLoadIdentity()
                glTranslatef(self.transv[0], self.transv[1], 0)
                glTranslatef(0, 0, self.transv[2])
                if(self.rot):
                    glMultMatrixd(build_rotmatrix(self.basequat))
                glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
                self.rot = 1
                self.initpos = None
        else:
            #mouse is moving without a button press
            p = event.GetPositionTuple()
            sz = self.GetClientSize()
            v = map(lambda m, w, b: b * m / w, p, sz, self.bedsize)
            v[1] = self.bedsize[1] - v[1]
            self.mousepos = v

    def rotate_shape(self, angle):
        """rotates acive shape
        positive angle is clockwise
        """
        name = self.parent.l.GetSelection()
        if name == wx.NOT_FOUND:
            return False
        name = self.parent.l.GetString(name)
        model = self.parent.models[name]
        model.rot += angle

    def wheel(self, event):
        """react to mouse wheel actions:
        rotate object
            with shift zoom viewport
        """
        z = event.GetWheelRotation()
        angle = 10
        if not event.ShiftDown():
            i = self.parent.l.GetSelection()

            if i < 0:
                try:
                    self.parent.setlayerindex(z)
                except:
                    pass
                return

            if z > 0:
                self.rotate_shape(angle / 2)
            else:
                self.rotate_shape(-angle / 2)
            return
        if z > 0:
            self.transv[2] += angle
        else:
            self.transv[2] -= angle

        glLoadIdentity()
        glTranslatef(*self.transv)
        if(self.rot):
            glMultMatrixd(build_rotmatrix(self.basequat))
        glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
        self.rot = 1

    def keypress(self, event):
        """gets keypress events and moves/rotates acive shape"""
        keycode = event.GetKeyCode()
        print keycode
        step = 5
        angle = 18
        if event.ControlDown():
            step = 1
            angle = 1
        #h
        if keycode == 72:
            self.move_shape((-step, 0))
        #l
        if keycode == 76:
            self.move_shape((step, 0))
        #j
        if keycode == 75:
            self.move_shape((0, step))
        #k
        if keycode == 74:
            self.move_shape((0, -step))
        #[
        if keycode == 91:
            self.rotate_shape(-angle)
        #]
        if keycode == 93:
            self.rotate_shape(angle)
        event.Skip()

    def update(self):
        while(1):
            dt = 0.05
            time.sleep(0.05)
            try:
                wx.CallAfter(self.Refresh)
            except:
                return

    def anim(self, obj):
        g = 50 * 9.8
        v = 20
        dt = 0.05
        basepos = obj.offsets[2]
        obj.offsets[2] += obj.animoffset
        while obj.offsets[2] > -1:
            time.sleep(dt)
            obj.offsets[2] -= v * dt
            v += g * dt
            if(obj.offsets[2] < 0):
                obj.scale[2] *= 1 - 3 * dt
        #return
        v = v / 4
        while obj.offsets[2] < basepos:
            time.sleep(dt)
            obj.offsets[2] += v * dt
            v -= g * dt
            obj.scale[2] *= 1 + 5 * dt
        obj.scale[2] = 1.0

    def create_objects(self):
        '''create opengl objects when opengl is initialized'''
        self.initialized = 1
        wx.CallAfter(self.Refresh)

    def drawmodel(self, m, n):
        batch = pyglet.graphics.Batch()
        stl = stlview(m.facets, batch=batch)
        m.batch = batch
        m.animoffset = 300
        #print m
        #threading.Thread(target = self.anim, args = (m, )).start()
        wx.CallAfter(self.Refresh)

    def update_object_resize(self):
        '''called when the window recieves only if opengl is initialized'''
        pass

    def draw_objects(self):
        '''called in the middle of ondraw after the buffer has been cleared'''
        if self.vpmat is None:
            return
        if not self.initialized:
            self.create_objects()

        #glLoadIdentity()
        #print list(self.pmat)
        if self.rot == 1:
            glLoadIdentity()
            glMultMatrixd(self.mvmat)
        else:
            glLoadIdentity()
            glTranslatef(*self.transv)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.2, 0.2, 0.2, 1))
        glBegin(GL_LINES)
        glNormal3f(0, 0, 1)
        rows = 10
        cols = 10
        zheight = 50
        for i in xrange(-rows, rows + 1):
            if i % 5 == 0:
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.6, 0.6, 0.6, 1))
            else:
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.2, 0.2, 0.2, 1))
            glVertex3f(10 * -cols, 10 * i, 0)
            glVertex3f(10 * cols, 10 * i, 0)
        for i in xrange(-cols, cols + 1):
            if i % 5 == 0:
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.6, 0.6, 0.6, 1))
            else:
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.2, 0.2, 0.2, 1))
            glVertex3f(10 * i, 10 * -rows, 0)
            glVertex3f(10 * i, 10 * rows, 0)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.6, 0.6, 0.6, 1))
        glVertex3f(10 * -cols, 10 * -rows, 0)
        glVertex3f(10 * -cols, 10 * -rows, zheight)
        glVertex3f(10 * cols, 10 * rows, 0)
        glVertex3f(10 * cols, 10 * rows, zheight)
        glVertex3f(10 * cols, 10 * -rows, 0)
        glVertex3f(10 * cols, 10 * -rows, zheight)
        glVertex3f(10 * -cols, 10 * rows, 0)
        glVertex3f(10 * -cols, 10 * rows, zheight)

        glVertex3f(10 * -cols, 10 * rows, zheight)
        glVertex3f(10 * cols, 10 * rows, zheight)
        glVertex3f(10 * cols, 10 * rows, zheight)
        glVertex3f(10 * cols, 10 * -rows, zheight)
        glVertex3f(10 * cols, 10 * -rows, zheight)
        glVertex3f(10 * -cols, 10 * -rows, zheight)
        glVertex3f(10 * -cols, 10 * -rows, zheight)
        glVertex3f(10 * -cols, 10 * rows, zheight)

        glEnd()
        glPushMatrix()
        glTranslatef(self.mousepos[0] - self.bedsize[0] / 2, self.mousepos[1] - self.bedsize[1] / 2, 0)
        glBegin(GL_TRIANGLES)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(1, 0, 0, 1))
        glNormal3f(0, 0, 1)
        glVertex3f(2, 2, 0)
        glVertex3f(-2, 2, 0)
        glVertex3f(-2, -2, 0)
        glVertex3f(2, -2, 0)
        glVertex3f(2, 2, 0)
        glVertex3f(-2, -2, 0)
        glEnd()
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.3, 0.7, 0.5, 1))
        #glTranslatef(0, 40, 0)
        glPopMatrix()
        glPushMatrix()
        glTranslatef(-100, -100, 0)
        glEnable(GL_LINE_SMOOTH)
        glEnable(GL_BLEND)
        glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glHint (GL_LINE_SMOOTH_HINT, GL_NICEST)
        glLineWidth (1.5)

        for i in self.parent.models.values():
            glPushMatrix()
            glTranslatef(*(i.offsets))
            glRotatef(i.rot, 0.0, 0.0, 1.0)
            glScalef(*i.scale)
            #glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.93, 0.37, 0.25, 1))
            glEnable(GL_COLOR_MATERIAL)

            if i.curlayer == -1:
                # curlayer == -1 means we are over the top.
                glLineWidth (0.8)
                [i.gc.layers[j].draw() for j in i.gc.layerlist]
            else:
                glLineWidth (0.6)

                tmpindex = i.gc.layerlist.index(i.curlayer)
                if tmpindex >= 5:
                    thin_layer = i.gc.layerlist[tmpindex - 5]
                    [i.gc.layers[j].draw() for j in i.gc.layerlist if j <= thin_layer]

                if tmpindex > 4:
                    glLineWidth (0.9)
                    i.gc.layers[i.gc.layerlist[tmpindex - 4]].draw()

                if tmpindex > 3:
                    glLineWidth (1.1)
                    i.gc.layers[i.gc.layerlist[tmpindex - 3]].draw()

                if tmpindex > 2:
                    glLineWidth (1.3)
                    i.gc.layers[i.gc.layerlist[tmpindex - 2]].draw()

                if tmpindex > 1:
                    glLineWidth (2.2)
                    i.gc.layers[i.gc.layerlist[tmpindex - 1]].draw()

                glLineWidth (3.5)
                i.gc.layers[i.curlayer].draw()
            glLineWidth (1.5)

            glDisable(GL_COLOR_MATERIAL)

            glPopMatrix()
        glPopMatrix()
        #print "drawn batch"


class GCFrame(wx.Frame):
    '''A simple class for using OpenGL with wxPython.'''

    def __init__(self, parent, ID, title, pos=wx.DefaultPosition,
            size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
        super(GCFrame, self).__init__(parent, ID, title, pos, (size[0] + 150, size[1]), style)

        class d:
            def GetSelection(self):
                return wx.NOT_FOUND
        self.p = self
        m = d()
        m.offsets = [0, 0, 0]
        m.rot = 0
        m.curlayer = -1
        m.scale = [1.0, 1.0, 1.0]
        m.batch = pyglet.graphics.Batch()
        m.gc = gcview([], batch=m.batch)
        self.models = {"GCODE": m}
        self.l = d()
        self.modelindex = 0
        self.GLPanel1 = TestGlPanel(self, size)

    def addfile(self, gcode=[]):
        self.models["GCODE"].gc.delete()
        self.models["GCODE"].gc = gcview(gcode, batch=self.models["GCODE"].batch)
        self.setlayerindex(None)

    def clear(self):
        self.models["GCODE"].gc.delete()
        self.models["GCODE"].gc = gcview([], batch=self.models["GCODE"].batch)

    def Show(self, arg=True):
        wx.Frame.Show(self, arg)
        self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] + 1))
        self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] - 1))
        self.Refresh()
        wx.FutureCall(500, self.GLPanel1.forceresize)
        #threading.Thread(target = self.update).start()
        #self.initialized = 0

    def setlayerindex(self, z):
        m = self.models["GCODE"]
        try:
            mlk = m.gc.layerlist
        except:
            mlk = []
        if z is None:
            self.modelindex = -1
        elif z > 0:
            if self.modelindex < len(mlk) - 1:
                if self.modelindex > -1:
                    self.modelindex += 1
            else:
                self.modelindex = -1
        elif z < 0:
            if self.modelindex > 0:
                self.modelindex -= 1
            elif self.modelindex == -1:
                self.modelindex = len(mlk)

        if self.modelindex >= 0:
            m.curlayer = mlk[self.modelindex]
            wx.CallAfter(self.SetTitle, "Gcode view, shift to move. Layer %d/%d, Z = %f" % (self.modelindex, len(mlk), m.curlayer))
        else:
            m.curlayer = -1
            wx.CallAfter(self.SetTitle, "Gcode view, shift to move view, mousewheel to set layer")



def main():
    app = wx.App(redirect=False)
    frame = GCFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size=(400, 400))
    import sys
    for filename in sys.argv:
        if ".gcode" in filename:
            frame.addfile(list(open(filename)))
        elif ".stl" in filename:
            #TODO: add stl here
            pass

    #frame = wx.Frame(None, -1, "GL Window", size=(400, 400))
    #panel = TestGlPanel(frame, size=(300,300))
    frame.Show(True)
    app.MainLoop()
    app.Destroy()

if __name__ == "__main__":
    #import cProfile
    #print cProfile.run("main()")
    main()
