#!/usr/bin/env python

__author__ = "Stefan Blanke (greenarrow) (greenarrow@users.sourceforge.net)"
__credits__ = ""
__license__ = "GPL 3.0"
__version__ = "0.8"
__licence__ = """
pyRepRap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

pyRepRap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with pyRepRap.  If not, see <http://www.gnu.org/licenses/>.
"""

# -*- coding: utf-8 -*-
# generated by wxGlade 0.6.1 on Wed Apr 23 11:49:08 2008

import wx, pygame, reprap, time, sys, os, threading
import replath.wxpygame as wxpygame
import reprap.shapeplotter, replath.plugins, reprap.toolpath
import replath

appTitle = "RepRap Plot"


plotterPlugins = replath.plugins.getPlugins(replath.plugins.PLUGIN_IMPORT)
outputPlugins = replath.plugins.getPlugins(replath.plugins.PLUGIN_OUTPUT)
toolheadPlugins = replath.plugins.getPlugins(replath.plugins.PLUGIN_TOOLHEAD)
plotterList = replath.plugins.listTitles(plotterPlugins)
outputList = replath.plugins.listTitles(outputPlugins)
toolheadList = replath.plugins.listTitles(toolheadPlugins)

if sys.platform=="win32":
    # Windows (taking path of this script)
    iconPath = os.path.join( os.getcwd(), "graphics" )
else:
    # Linux
    iconPath = "/usr/local/share/replath/icons/"

appIcon = os.path.join(iconPath, "reprap.png")



# Feedback action definitions
EVT_FBM_ID = wx.NewId()
FB_STATUS = 1
FB_COMPLETE = 2
FB_ABORTED = 3
FB_MESSAGE = 4

# Feedback message event object. Always used by feedbackmessagehandler (external plotters do not use it)
class FeedbackMessageEvent(wx.PyEvent):
    """Simple event to carry arbitrary result data."""
    def __init__(self, action, data):
        """Init Result Event."""
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_FBM_ID)
        self.action = action
        self.data = data

# Passed to plotters so they can give feedback info to the gui (including when they are finished)
class FeedbackMessageHandler:
    def __init__(self, parent):
        self.parent = parent
        parent.Connect(-1, -1, EVT_FBM_ID, self.onEvent)
    
    def onEvent(self, event):
        if event.action == FB_STATUS:
            self.parent.setStatusBar(event.data)
        elif event.action == FB_COMPLETE:
            self.parent.plotComplete()
        elif event.action == FB_ABORTED:
            self.parent.plotAborted()
        elif event.action == FB_MESSAGE:
            dlg = wx.MessageDialog(self.parent, str(event.data) + '\n', caption = appTitle, style = wx.OK)
            #dlg = wx.MessageDialog(self.parent, 'moo\n', caption = 'moo', style = wx.OK)
            dlg.ShowModal()
    
    def plotComplete(self):
        wx.PostEvent( self.parent, FeedbackMessageEvent(FB_COMPLETE, None) )
    
    def setStatus(self, text):
        wx.PostEvent( self.parent, FeedbackMessageEvent(FB_STATUS, text) )
    
    def setProgressPopup(self, text, progress = 0):
        #if text:
        #    #show window
        #    self.progressDialog.progress.SetValue(int(progress))
        #else:
        #    #hide window
        pass
    
    def updateProgressPopup(self, progress):
        #self.progressDialog.progress.SetValue(int(progress))
        pass
        
    def showMessagePopup(self, text):
        wx.PostEvent( self.parent, FeedbackMessageEvent(FB_MESSAGE, text) )
        
    def aborted(self):
        wx.PostEvent( self.parent, FeedbackMessageEvent(FB_ABORTED, None) )

# content of this block not found: did you rename this class?
pass

# Dialog class for preferences
class PreferencesDialog(wx.Dialog):
    def __init__(self, *args, **kwds):
        # begin wxGlade: PreferencesDialog.__init__
        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
        wx.Dialog.__init__(self, *args, **kwds)
        self.notebook_main = wx.Notebook(self, -1, style=wx.NB_LEFT)
        self.panel_toolheads = wx.Panel(self.notebook_main, -1)
        self.panel_outputs = wx.Panel(self.notebook_main, -1)
        self.panel_inputs = wx.Panel(self.notebook_main, -1)
        self.panel_general = wx.Panel(self.notebook_main, -1)
        self.sizer_7_staticbox = wx.StaticBox(self, -1, "")
        self.sizer_9_staticbox = wx.StaticBox(self.panel_general, -1, "")
        self.label_8 = wx.StaticText(self.panel_general, -1, "Plot offset (mm) : ")
        self.label_9 = wx.StaticText(self.panel_general, -1, "X : ")
        self.text_offsetX = wx.TextCtrl(self.panel_general, -1, "")
        self.label_10 = wx.StaticText(self.panel_general, -1, "Y :")
        self.text_offsetY = wx.TextCtrl(self.panel_general, -1, "")
        self.label_11 = wx.StaticText(self.panel_general, -1, "Fill density (lines / mm) :")
        self.text_fillDensity = wx.TextCtrl(self.panel_general, -1, "")
        self.label_16 = wx.StaticText(self.panel_general, -1, "Circle resolution (lines / mm in c) :")
        self.text_circleResolution = wx.TextCtrl(self.panel_general, -1, "")
        self.label_1 = wx.StaticText(self.panel_general, -1, "Delay between lines  (seconds) :")
        self.text_lineDelay = wx.TextCtrl(self.panel_general, -1, "")
        self.label_2 = wx.StaticText(self.panel_general, -1, "Zoom step (factor) :")
        self.text_zoomStep = wx.TextCtrl(self.panel_general, -1, "")
        self.label_3 = wx.StaticText(self.panel_general, -1, "Grid division (mm) :")
        self.text_gridDivision = wx.TextCtrl(self.panel_general, -1, "")
        self.checkbox_debug = wx.CheckBox(self.panel_general, -1, "Debug")
        self.notebook_inputs = wx.Notebook(self.panel_inputs, -1, style=wx.NB_LEFT)
        self.notebook_outputs = wx.Notebook(self.panel_outputs, -1, style=wx.NB_LEFT)
        self.notebook_toolheads = wx.Notebook(self.panel_toolheads, -1, style=wx.NB_LEFT)
        self.button_Ok = wx.Button(self, wx.ID_OK, "")
        self.button_Cancel = wx.Button(self, wx.ID_CANCEL, "")

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_BUTTON, self.hanBtnOk, self.button_Ok)
        self.Bind(wx.EVT_BUTTON, self.hanBtnCancel, self.button_Cancel)
        # end wxGlade

    def __set_properties(self):
        # begin wxGlade: PreferencesDialog.__set_properties
        self.SetTitle("RepRap Gerber Plotter - Preferences")
        _icon = wx.EmptyIcon()
        _icon.CopyFromBitmap(wx.Bitmap(appIcon))
        self.SetIcon(_icon)
        # end wxGlade

    def __do_layout(self):
        print "panel load"
        # Dynamically load panels from plugins
        self.preferencePanels = []
        for plugin in outputPlugins:
            if dir(plugin).count('PreferencesPanel'):
                newPanel = plugin.PreferencesPanel(self.notebook_outputs)
                self.preferencePanels.append( newPanel )
                self.notebook_outputs.AddPage(newPanel, plugin.Title)
        for plugin in toolheadPlugins:
            if dir(plugin).count('PreferencesPanel'):
                newPanel = plugin.PreferencesPanel(self.notebook_toolheads)
                self.preferencePanels.append( newPanel )
                self.notebook_toolheads.AddPage(newPanel, plugin.Title)
        for plugin in plotterPlugins:
            if dir(plugin).count('PreferencesPanel'):
                newPanel = plugin.PreferencesPanel(self.notebook_inputs)
                self.preferencePanels.append( newPanel )
                self.notebook_inputs.AddPage(newPanel, plugin.Title)
        
        # begin wxGlade: PreferencesDialog.__do_layout
        sizer_7 = wx.StaticBoxSizer(self.sizer_7_staticbox, wx.VERTICAL)
        sizer_5 = wx.BoxSizer(wx.VERTICAL)
        sizer_7_copy = wx.BoxSizer(wx.HORIZONTAL)
        sizer_4 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        sizer_9 = wx.StaticBoxSizer(self.sizer_9_staticbox, wx.VERTICAL)
        grid_sizer_2 = wx.FlexGridSizer(7, 3, 4, 0)
        grid_sizer_1 = wx.FlexGridSizer(4, 11, 4, 0)
        grid_sizer_1.Add(self.label_8, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.label_9, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.text_offsetX, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.label_10, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.text_offsetY, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.AddGrowableCol(4)
        grid_sizer_1.AddGrowableCol(8)
        sizer_9.Add(grid_sizer_1, 0, wx.ALL|wx.EXPAND, 10)
        grid_sizer_2.Add(self.label_11, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.text_fillDensity, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.label_16, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.text_circleResolution, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.label_1, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.text_lineDelay, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.label_2, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.text_zoomStep, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.label_3, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.text_gridDivision, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add(self.checkbox_debug, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_2.AddGrowableCol(2)
        sizer_9.Add(grid_sizer_2, 1, wx.ALL|wx.EXPAND, 10)
        sizer_2.Add(sizer_9, 1, wx.ALL|wx.EXPAND, 10)
        self.panel_general.SetSizer(sizer_2)
        sizer_1.Add(self.notebook_inputs, 1, wx.ALL|wx.EXPAND, 10)
        self.panel_inputs.SetSizer(sizer_1)
        sizer_3.Add(self.notebook_outputs, 1, wx.ALL|wx.EXPAND, 10)
        self.panel_outputs.SetSizer(sizer_3)
        sizer_4.Add(self.notebook_toolheads, 1, wx.ALL|wx.EXPAND, 10)
        self.panel_toolheads.SetSizer(sizer_4)
        self.notebook_main.AddPage(self.panel_general, "General")
        self.notebook_main.AddPage(self.panel_inputs, "Plotter Plugins")
        self.notebook_main.AddPage(self.panel_outputs, "Output Plugins")
        self.notebook_main.AddPage(self.panel_toolheads, "Toolhead Plugins")
        sizer_5.Add(self.notebook_main, 1, wx.EXPAND, 0)
        sizer_5.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
        sizer_7_copy.Add(self.button_Ok, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        sizer_7_copy.Add(self.button_Cancel, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
        sizer_5.Add(sizer_7_copy, 0, wx.EXPAND, 0)
        sizer_7.Add(sizer_5, 1, wx.ALL|wx.EXPAND|wx.ADJUST_MINSIZE, 16)
        self.SetSizer(sizer_7)
        sizer_7.Fit(self)
        self.Layout()
        self.Centre()
        # end wxGlade
        

        
    def setPrefHandler(self, pref):
        self.preferences = pref

    # Set values of frame control
    def setPrefValues(self):
        self.text_offsetX.SetValue( str(self.preferences.pref_plotOffsetX) )
        self.text_offsetY.SetValue( str(self.preferences.pref_plotOffsetY) )
        self.text_fillDensity.SetValue( str(self.preferences.pref_fillDensity) )
        self.text_lineDelay.SetValue( str(self.preferences.pref_lineDelay) )
        self.checkbox_debug.SetValue( self.preferences.pref_debug )
        self.text_circleResolution.SetValue( str(self.preferences.pref_circleResolution) )
        self.text_gridDivision.SetValue( str(self.preferences.pref_gridDivision) )
        self.text_zoomStep.SetValue( str(self.preferences.pref_zoomStep) )
    
    def getPreferences(self):
        return self.preferences

    # User clicked OK
    def hanBtnOk(self, event): # wxGlade: PreferencesDialog.<event_handler>
        self.preferences.pref_plotOffsetX = float( self.text_offsetX.GetValue() )
        self.preferences.pref_plotOffsetY = float( self.text_offsetY.GetValue() )
        self.preferences.pref_fillDensity = int( self.text_fillDensity.GetValue() )
        self.preferences.pref_lineDelay = float( self.text_lineDelay.GetValue() )
        self.preferences.pref_debug = self.checkbox_debug.GetValue()
        self.preferences.pref_circleResolution = float( self.text_circleResolution.GetValue() )
        self.preferences.pref_gridDivision = float( self.text_gridDivision.GetValue() )
        self.preferences.pref_zoomStep = float( self.text_zoomStep.GetValue() )
        
        for p in self.preferencePanels:
            p.savePrefValues()
        self.EndModal(wx.OK) 
    
    # User clicked Cancel
    def hanBtnCancel(self, event): # wxGlade: PreferencesDialog.<event_handler>
        self.preferences = False
        self.EndModal(wx.CANCEL)
# end of class PreferencesDialog

"""
# A set of lines for drawing on the screen  TODO - put draw back in
class lineSet:
    def __init__(self, parent, colour):
        self.parent = parent
        self.colour = colour
        self.clear()
    def setColour(colour):
        self.colour = colour
        self.tableChanged = True
    def addLine(self, line):
        self.lines.append(line)
        #self.parent.draw()      #not ideal
        #pygame.display.flip()
        self.tableChanged = True
    def getLines(self):
        return self.lines
    def clear(self):
        self.lines = []
        self.tableChanged = True
    def hasChanged(self):
        if self.tableChanged:
            self.tableChanged = False
            return True
        else:
            return False
"""

# Class for pygame canvas
class DrawCanvas(wxpygame.wxSDLPanel):
    def __init__( self, parent, ID=-1 ):
        wxpygame.wxSDLPanel.__init__(self, parent, ID)
        self.previewScale = 5.0
        self.offsetX, self.offsetY = 0, 0
        
        self.black = 0, 0, 0
        self.white = 255, 255, 255
        self.grey = 160, 160, 160
        self.greyGreen = 170, 195, 170
        self.darkGrey = 140, 140, 140
        
        self.backroundColour = self.white
        self.baseColour = 166, 211, 166
        self.gridColour = self.greyGreen
        self.previewColour = self.darkGrey
        self.plotColour = self.black
        
        pygame.font.init()
        self.font = pygame.font.Font(None, 17)
        self.drawSurface = pygame.Surface( ( 0, 0 ) )
        self.drawn = False
    
    def setParent(self, parent):
        self.parent = parent
        self.fullRedraw()
    
    def drawText( self, surface, x, y, text, font ):
        text = font.render( text, True, (255, 255, 255), (159, 182, 205) )
        textRect = text.get_rect()
        textRect.centerx = x
        textRect.centery = y
        surface.blit(text, textRect)
    
    # Draw plot to widget
    def draw( self ):
        surface = self.getSurface()
        w, h = self.GetSizeTuple()
        if not surface is None:
            surface.fill( self.backroundColour )
            if self.drawn:
                surface.blit( self.drawSurface, (self.offsetX, -self.offsetY) )#limit region to visible - is this needed?
            
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
            pygame.display.flip()
    
    def fullRedraw(self):
        if self.parent.currentToolpath:
            fileOffsetX, fileOffsetY = self.parent.currentToolpath.offsetX, self.parent.currentToolpath.offsetY
            # Find limits
            #if len(self.polygons) > 0:
            minX, minY, maxX, maxY = 1000000, 1000000, -1000000, -1000000
            if len(self.parent.currentToolpath.layers):
                for layer in self.parent.currentToolpath.layers:
                    for poly in layer.polygons:
                        for p in poly.points:
                            minX = min(minX, p.x + fileOffsetX)
                            minY = min(minY, p.y + fileOffsetY)
                            maxX = max(maxX, p.x + fileOffsetX)
                            maxY = max(maxY, p.y + fileOffsetY)
            else:
                minX, minY, maxX, maxY = 0, 0, 0, 0
            border = int(self.parent.preferences.pref_gridDivision * self.previewScale / 2)
            w, h = self.GetSizeTuple()
            width, height = int(maxX * self.previewScale) + (2 * border), int(maxY * self.previewScale) + (2 *border)
            #print "wh", width, height
            if width > 0 and height > 0:
                self.drawSurface = pygame.Surface( ( width, height ) )
                self.drawRect = 0, 0, width, height
                
                # Only draw if we have something to show
                #if len(self.polygons) > 0:
                # Draw background
                pygame.draw.rect( self.drawSurface, self.baseColour, self.drawRect, 0)
                # Draw grid
                for x in frange( 0, maxX, self.parent.preferences.pref_gridDivision ):
                    pygame.draw.line( self.drawSurface, self.gridColour, ( int( float(x) * self.previewScale ) + border , border ), ( int( float(x) * self.previewScale ) + border , height - border ) )
                for y in frange( 0, maxY, self.parent.preferences.pref_gridDivision ):
                    pygame.draw.line( self.drawSurface, self.gridColour, ( border, height - border - int( float(y) * self.previewScale ) ), ( width - border, height - border - int( float(y) * self.previewScale ) ) )
                
                # Draw axis labels
                pygame.draw.line( self.drawSurface, self.black, (border, height - border), (border + 50, height - border) )
                pygame.draw.line( self.drawSurface, self.black, (border, height - border), (border, height - 50 - border ) )
                self.drawText( self.drawSurface, border + 25, height - border , "X", self.font )
                self.drawText( self.drawSurface, border, height - border - 25, "Y", self.font )
                #else:
                #    # Draw blank white box
                #    pygame.draw.rect( self.drawSurface, self.white, self.drawRect, 0)
                
                for layer in self.parent.currentToolpath.layers:
                    # Draw polygons
                    for poly in layer.polygons:
                        #print "plotting poly", poly, len(poly.points)
                        oldX, oldY = poly.points[0].x, poly.points[0].y
                        for ip, p in enumerate(poly.points[ 1: ]):
                            if ip < poly.pointsPlotted:
                                lineColour = self.black
                            else:
                                lineColour = self.previewColour
                            x1, y1, x2, y2 = self.scaleRect( (oldX + fileOffsetX, oldY + fileOffsetY, p.x + fileOffsetX, p.y + fileOffsetY), self.previewScale )
                            pygame.draw.line( self.drawSurface, lineColour, (x1 + border, height - y1 - border), (x2 + border, height - y2  -border) )
                            oldX, oldY = p.x, p.y
                        # Final line if closed
                        if poly.closed:
                            x, y = poly.points[0].x, poly.points[0].y
                            x1, y1, x2, y2 = self.scaleRect( (oldX + fileOffsetX, oldY + fileOffsetY, x + fileOffsetX, y + fileOffsetY), self.previewScale )
                            pygame.draw.line( self.drawSurface, self.previewColour, (x1 + border, height - y1 - border), (x2 + border, height - y2  -border) )
                self.drawn = True
                self.draw()
    
    # Simple coordiante manipulation
    def scaleRect( self, rect, scale ):
        x1, y1, x2, y2 = rect
        scaleX = float(scale)
        scaleY = float(scale)
        return int( float(x1) * scale ), int( float(y1) * scale ), int( float(x2) * scale ), int( float(y2) * scale )
    
    def offsetRect( self, rect, x, y ):
        x1, y1, x2, y2 = rect
        return x1 + x, y1 + y, x2, y2
    
    def offsetLine(self, rect, x, y):
        x1, y1, x2, y2 = rect
        return x1 + x, y1 + y, x2 + x, y2 + y
    
    def centreView(self, x, y):
        widthV, heightV = self.GetSizeTuple()
        null, null, widthD, heightD = self.drawRect
        self.offsetX = (widthV - widthD + x) / 2
        self.offsetY = (heightV - heightD + y) / -2
    
    # Change draw offset to allow dragging of plot              
    def MouseMove(self, event):
        w, h = self.GetSizeTuple()
        if event.LeftIsDown():
            x, y = event.GetPosition()
            if event.Dragging():
                self.offsetX, self.offsetY = x + self.moveDeltaX, self.moveDeltaY - y
    
    # Record offsets for use in drag movement
    def OnMouseDown(self, event):
        w, h = self.GetSizeTuple()
        x, y = event.GetPosition()
        #print x, y
        self.moveDeltaX, self.moveDeltaY = self.offsetX - x, self.offsetY + y
    
    def OnMouseUp(self, event):
        pass
    
    # When mouse wheel moved, zoom in or out
    def OnMouseWheel(self, event):
        w, h = self.GetSizeTuple()
        centreX, centreY = w / 2, h / 2
        x, y = event.GetPosition()
        #deltaX, deltaY = centreX - x, y - centreY
        
        if event.GetWheelRotation() > 0:
            self.previewScale = self.previewScale + self.parent.preferences.pref_zoomStep
        elif self.previewScale >= self.parent.preferences.pref_zoomStep:
            self.previewScale = self.previewScale - self.parent.preferences.pref_zoomStep
        self.fullRedraw()
        #null, null, widthD, heightD = self.drawRect
        #self.centreView(x - self.offsetX, h - y - self.offsetY)
        #self.centreView(100, 100)


# Main frame class
class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # Create preference handler that stores preferences on itself
        self.preferences = replath.preferences.PreferenceHandler( False, "pyRepRap.conf" )
        # default values for preferences
        self.preferences.pref_plotOffsetX = 10.0
        self.preferences.pref_plotOffsetY = 10.0
        self.preferences.pref_fillDensity = 5
        self.preferences.pref_lineDelay = 0.0
        self.preferences.pref_debug = False
        self.preferences.pref_circleResolution = 3.0
        self.preferences.pref_gridDivision = 5.0
        self.preferences.pref_zoomStep = 1
        
        self.preferences.load()
        
        self.fileName = False
        self.currentPlotter = False
        self.currentToolpath = False
        
        # begin wxGlade: MyFrame.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.panel_1 = wx.Panel(self, -1)
        
        # Menu Bar
        self.frmMain_menubar = wx.MenuBar()
        self.mnuFile = wx.Menu()
        self.mnuOpen = wx.MenuItem(self.mnuFile, 0, "&Open\tCtrl+O", "", wx.ITEM_NORMAL)
        self.mnuFile.AppendItem(self.mnuOpen)
        self.mnuFile.AppendSeparator()
        self.mnuPlot = wx.MenuItem(self.mnuFile, 2, "&Plot\tCtrl+P", "", wx.ITEM_NORMAL)
        self.mnuFile.AppendItem(self.mnuPlot)
        self.mnuFile.AppendSeparator()
        self.mnuQuit = wx.MenuItem(self.mnuFile, 1, "&Quit\tCtrl+Q", "", wx.ITEM_NORMAL)
        self.mnuFile.AppendItem(self.mnuQuit)
        self.frmMain_menubar.Append(self.mnuFile, "&File")
        wxglade_tmp_menu = wx.Menu()
        wxglade_tmp_menu.Append(10, "Pr&eferences", "", wx.ITEM_NORMAL)
        self.frmMain_menubar.Append(wxglade_tmp_menu, "&Edit")
        wxglade_tmp_menu = wx.Menu()
        self.mnuAbout = wx.MenuItem(wxglade_tmp_menu, 40, "&About", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendItem(self.mnuAbout)
        self.frmMain_menubar.Append(wxglade_tmp_menu, "&Help")
        self.SetMenuBar(self.frmMain_menubar)
        # Menu Bar end
        self.frmMain_statusbar = self.CreateStatusBar(1, 0)
        
        # Tool Bar
        self.frmMain_toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL|wx.TB_TEXT)
        self.SetToolBar(self.frmMain_toolbar)
        self.frmMain_toolbar.AddLabelTool(100, "Open", (wx.Bitmap( os.path.join(iconPath, 'document-open.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "Open file", "")
        self.frmMain_toolbar.AddSeparator()
        self.frmMain_toolbar.AddLabelTool(110, "Stop", (wx.Bitmap( os.path.join(iconPath, 'media-playback-stop.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "Stop current plotting action", "")
        self.frmMain_toolbar.AddSeparator()
        self.frmMain_toolbar.AddLabelTool(120, "Plot", (wx.Bitmap( os.path.join(iconPath, 'media-playback-start.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "Plot to output plugin", "")
        self.frmMain_toolbar.AddSeparator()
        self.frmMain_toolbar.AddLabelTool(130, "Preferences", (wx.Bitmap( os.path.join(iconPath, 'configure.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "", "")
        # Tool Bar end
        self.static_line_1 = wx.StaticLine(self.panel_1, -1)
        self.label_22 = wx.StaticText(self.panel_1, -1, "Output :")
        self.choice_output = wx.Choice(self.panel_1, -1, choices=[])
        self.label_23 = wx.StaticText(self.panel_1, -1, "Toolhead :")
        self.choice_toolhead = wx.Choice(self.panel_1, -1, choices=[])
        self.static_line_2 = wx.StaticLine(self.panel_1, -1)
        self.pygameCanvas = DrawCanvas(self.panel_1, -1)

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_MENU, self.onClickOpen, self.mnuOpen)
        self.Bind(wx.EVT_MENU, self.onClickPlot, self.mnuPlot)
        self.Bind(wx.EVT_MENU, self.onClickQuit, self.mnuQuit)
        self.Bind(wx.EVT_MENU, self.onClickPreferences, id=10)
        self.Bind(wx.EVT_MENU, self.onClickAbout, self.mnuAbout)
        self.Bind(wx.EVT_TOOL, self.onClickOpen, id=100)
        self.Bind(wx.EVT_TOOL, self.onClickStop, id=110)
        self.Bind(wx.EVT_TOOL, self.onClickPlot, id=120)
        self.Bind(wx.EVT_TOOL, self.onClickPreferences, id=130)
        # end wxGlade
        
        #self.Bind(wx.EVT_CLOSE, self.onCloseWindow)
        self.pygameCanvas.setParent(self)
        self.setStatusBar("RepRap Plot")
    
    def __set_properties(self):
        # begin wxGlade: MyFrame.__set_properties
        self.SetTitle("RepRap Plotter")
        _icon = wx.EmptyIcon()
        _icon.CopyFromBitmap(wx.Bitmap(appIcon))
        self.SetIcon(_icon)
        self.SetSize((869, 579))
        self.frmMain_statusbar.SetStatusWidths([-1])
        # statusbar fields
        frmMain_statusbar_fields = ["frmMain_statusbar"]
        for i in range(len(frmMain_statusbar_fields)):
            self.frmMain_statusbar.SetStatusText(frmMain_statusbar_fields[i], i)
        self.frmMain_toolbar.SetToolBitmapSize((32, 32))
        self.frmMain_toolbar.Realize()
        # end wxGlade
        
        # Load plugin names into choice lists
        self.choice_output.Append("None")
        for p in outputList:
            self.choice_output.Append(p)
        self.choice_output.SetSelection(0)
        
        for p in toolheadList:
            self.choice_toolhead.Append(p)
        self.choice_toolhead.SetSelection(0)

    def __do_layout(self):
        # begin wxGlade: MyFrame.__do_layout
        sizer_6 = wx.BoxSizer(wx.VERTICAL)
        sizer_main = wx.BoxSizer(wx.VERTICAL)
        sizer_19 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_main.Add(self.static_line_1, 0, wx.ALL|wx.EXPAND, 5)
        sizer_19.Add((10, 1), 0, wx.ADJUST_MINSIZE, 0)
        sizer_19.Add(self.label_22, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        sizer_19.Add((10, 1), 0, wx.ADJUST_MINSIZE, 0)
        sizer_19.Add(self.choice_output, 0, wx.ADJUST_MINSIZE, 0)
        sizer_19.Add((20, 1), 0, wx.ADJUST_MINSIZE, 0)
        sizer_19.Add(self.label_23, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        sizer_19.Add((10, 1), 0, wx.ADJUST_MINSIZE, 0)
        sizer_19.Add(self.choice_toolhead, 0, wx.ADJUST_MINSIZE, 0)
        sizer_main.Add(sizer_19, 0, wx.EXPAND, 0)
        sizer_main.Add(self.static_line_2, 0, wx.ALL|wx.EXPAND, 5)
        sizer_main.Add(self.pygameCanvas, 5, wx.EXPAND, 1)
        self.panel_1.SetSizer(sizer_main)
        sizer_6.Add(self.panel_1, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_6)
        self.Layout()
        self.Centre()
        # end wxGlade
    
    def plotComplete(self):
        self.pygameCanvas.fullRedraw()
        self.pygameCanvas.centreView(0, 0)
        self.currentPlotter.join()
        self.currentPlotter = False
        self.setStatusBar("Plot Complete")
        #dlg = wx.MessageDialog(self, "Plot Complete\n", caption = appTitle, style = wx.OK)
        #dlg.ShowModal()
        
    def plotAborted(self):
        self.pygameCanvas.fullRedraw()
        if self.currentPlotter:
            self.currentPlotter.join()
        self.currentPlotter = False
        self.setStatusBar("Plot aborted")
        dlg = wx.MessageDialog(self, "Plot aborted\n", caption = appTitle, style = wx.OK)
        dlg.ShowModal()
    
    # Produces strings for file formats in wx file dialog e.g. 'Gerber Files (*.pho)|*.pho'
    def dialogFilterString(self, extensions, name):
        rText = name + " ("
        for e in extensions:
            rText += '*' + e + ';'
        rText = rText[ :-1 ]
        rText += ")|"
        for e in extensions:
            rText += '*' + e + ';'
        # Removed as windows did not like having no end chr
        rText = rText[ :-1 ]
        return rText
    
    def importPlot(self, fileName = False, quiet = False):
        if fileName:
            self.fileName = fileName
        else:
            self.fileName = False
            filters = ""
            allFormats = []
            # Produce strings for file formats for wx file dialog
            for p in plotterPlugins:
                filters += self.dialogFilterString( p.SupportedFileExtensions, p.FileTitle ) + '|'
                allFormats += p.SupportedFileExtensions
            # Produce all supported formats string, put it at the start of the list
            filters = self.dialogFilterString( allFormats, "All Supported Files" ) + '|' + filters
            filters = filters[ :-1 ]
            #print "ff [" + filters + "]"
            dialog = wx.FileDialog ( None, message = 'Open Gerber File', wildcard = filters, style = wx.OPEN )
            if dialog.ShowModal() == wx.ID_OK:
                self.fileName = dialog.GetPath()
                self.setStatusBar( "Opened File '" + self.fileName + "'" )
            dialog.Destroy()
        
        if self.fileName or fileName:
            self.currentToolpath = reprap.toolpath.Object()
            self.currentToolpath.offsetX, self.currentToolpath.offsetY = self.preferences.pref_plotOffsetX, self.preferences.pref_plotOffsetY
            self.currentPlotter = False
            plugin = False
            cancel = False
            feedbackHandler = FeedbackMessageHandler(self)
            # Find a plugin that claims to handle files with the extension
            for p in plotterPlugins:
                # TODO - change to look for last dot not first, as it gets confused with network locations
                #(fileBaseName, fileExtension)=os.path.splitext(fileName)
                extension = self.fileName[ self.fileName.find('.'): ].lower()
                if p.SupportedFileExtensions.count( extension ) > 0:
                    # # Check if the plotter has a preferences dialog and show it if it has one
                    if dir(p).count('PreferencesDialog') and not quiet:
                        plotPrefDialog = p.PreferencesDialog(None, -1, "")
                        app.SetTopWindow(plotPrefDialog)
                        if plotPrefDialog.ShowModal() == wx.OK:
                            plugin = p
                        else:
                            cancel = True
                        break
                    else:
                        plugin = p
            
            # If a suitable plugin has been found
            if plugin:
                # Create plotter
                self.currentPlotter = plugin.plotter(self.fileName, self.currentToolpath,
                                                feedbackHandler = feedbackHandler,
                                                arcResolution = self.preferences.pref_circleResolution,
                                                fillDensity = self.preferences.pref_fillDensity,
                                                debug = self.preferences.pref_debug,
                                               )
                if self.currentPlotter:
                    # Start plotter thread
                    self.currentPlotter.start()
            elif not cancel :
                dlg = wx.MessageDialog(self, "File format unsupported\n", caption = appTitle, style = wx.OK)
                dlg.ShowModal()
    
    def exportPlot(self, fileName = False):
            #self.setStatusBar(  "Plotting File '" + self.fileName + "'" )
            # Use user selected output module
            if self.choice_output.GetSelection() > 0:
                outputs = [ self.choice_output.GetSelection() - 1 ]
            else:
                outputs = []
            feedbackHandler = FeedbackMessageHandler(self)
            toolhead = toolheadPlugins[ self.choice_toolhead.GetSelection() ].tool()
            outputPlotters = []
            for o in outputs:
                outputFilename = None
                if outputPlugins[o].FileOutput:
                    saveDialog = wx.FileDialog(self, message='Save file as...', wildcard=outputPlugins[o].Wildcard, style=wx.SAVE | wx.OVERWRITE_PROMPT)       #defaultDir=dir, defaultFile='',
                    if saveDialog.ShowModal() == wx.ID_OK:
                        outputFilename = saveDialog.GetPath()
                    else:
                        outputFilename = None
                    saveDialog.Destroy()
                
                self.currentPlotter = outputPlugins[o].output(self.currentToolpath, toolhead, feedbackHandler, outputFilename)
                self.currentPlotter.start()
    
    
    def setStatusBar(self, text):
        self.frmMain_statusbar.SetStatusText( text, 0 )
 
    def onClickOpen(self, event): # wxGlade: MyFrame.<event_handler>
        if self.currentPlotter:
            dlg = wx.MessageDialog(self, "Current action must be stopped before opening a file.\n", caption = appTitle, style = wx.OK)
            dlg.ShowModal()
        else:
            self.importPlot()

    #def onCloseWindow(self, evt):
    #    #self.Destroy()
    #    wx.Exit()

    def onClickQuit(self, event): # wxGlade: MyFrame.<event_handler>
        wx.Exit()

    def onClickPreferences(self, event): # wxGlade: MyFrame.<event_handler>
        dialogPref = PreferencesDialog(None, -1, "")
        app.SetTopWindow(dialogPref)
        
        dialogPref.setPrefHandler( self.preferences )
        dialogPref.setPrefValues()
        dialogPref.ShowModal()
        
        self.pygameCanvas.fullRedraw()
        
        dialogPref.Destroy()
        self.preferences.save()

    def onClickAbout(self, event): # wxGlade: MyFrame.<event_handler>
        description = "RepRap Plotter is a program for plotting Gerber and CAD files on a RepRap machine."
        info = wx.AboutDialogInfo()
        info.SetIcon(wx.Icon(appIcon, wx.BITMAP_TYPE_PNG))
        info.SetName('About RepRap Plotter')
        info.SetVersion(__version__)
        info.SetDescription(description)
        #info.SetCopyright('')
        info.SetLicence(__licence__)
        info.AddDeveloper(__author__)
        wx.AboutBox(info)

    def onClickStop(self, event): # wxGlade: MyFrame.<event_handler>
        # tell plotter thread to quit
        if self.currentPlotter:
            self.currentPlotter.terminate()
        else:
            dlg = wx.MessageDialog(self, "Nothing is currently being plotted.\n", caption = appTitle, style = wx.OK)
            dlg.ShowModal()

    def onClickPlot(self, event): # wxGlade: MyFrame.<event_handler>
        if self.currentPlotter:
            dlg = wx.MessageDialog(self, "Current action must be stopped before plotting.\n", caption = appTitle, style = wx.OK)
            dlg.ShowModal()
        else:
            self.exportPlot()


# end of class MyFrame


# Range function accepting floats (by Dinu Gherman)
def frange(start, end=None, inc=None):
    if end == None:
        end = start + 0.0
        start = 0.0
    if inc == None:
        inc = 1.0
    L = []
    while 1:
        next = start + len(L) * inc
        if inc > 0 and next >= end:
            break
        elif inc < 0 and next <= end:
            break
        L.append(next)
    return L


# Strip quotations
def stripQuotes(text):
    if (text[0] == '"' and text[-1] == '"') or (text[0] == "'" and text[-1] == "'"):
        return text[ 1:-1 ]
    else:
        return text


showGui = True
if len(sys.argv) > 1:
    if sys.argv.count("--nogui") or sys.argv.count("--g") or sys.argv.count("--help") or sys.argv.count("-h") > 0:
        showGui = False


if __name__ == "__main__":
    if showGui:
        app = wx.PySimpleApp(0)
        wx.InitAllImageHandlers()
        frmMain = MyFrame(None, -1, "")
        app.SetTopWindow(frmMain)
        frmMain.Show()
        # Command line arg plot file temp TODO proper

        if sys.argv.count("--quiet") or sys.argv.count("--q"):
            quiet = True
        else:
            quiet = False
        
        if len(sys.argv) > 1:
            frmMain.importPlot( stripQuotes(sys.argv[-1]), quiet )


        app.MainLoop()
    else:
        print "TODO : Command line only mode"
        if sys.argv.count("--help"):
            print "Usage: reprapplot [OPTIONS] [FILE]..."
            print "Option          GNU long option         Meaning"
            print " -h             --help                  Show this message"
            print " -g             --nogui                 Don't show GUI"
            print " -q             --quiet                 Don't ask questions"




