import matplotlib
matplotlib.use("WXAgg")
matplotlib.interactive(True)
from matplotlib.backends.backend_wxagg import Toolbar, FigureCanvasWxAgg
from matplotlib.figure import Figure
from matplotlib.axes import Subplot

import wx
import wx.lib.newevent
import threading
import time
import ezdaq
import os

ID_STARTBUTTON = 101
ID_STOPBUTTON = 102
ID_CLEARBUTTON = 103
ID_OKBUTTON = 104
ID_CONFIGUREBUTTON = 105
X_INIT = -.1
Y_INIT = .1
ARRAY_LIM = 100000

# this creates an UpdateCanvasEvent even class that will get created
# and sent from the worker thread to the main thread (which is the
# only place that updates to the GUI should happen...lest there be crashes)
(UpdateCanvasEvent, EVT_UPDATE_CANVAS) = wx.lib.newevent.NewEvent()

class MainWindow(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Free Period Calculation")

        self.arrayLock = threading.Semaphore(1)
        self.canvasLock = threading.Semaphore(1)

        #create the preferences window
        self.prefwindow = self.createPreferencesWindow()

        self.figure = Figure((5, 4), 75)
        self.canvas = FigureCanvasWxAgg(self, -1, self.figure)
        self.toolbar = Toolbar(self.canvas)
        self.toolbar.Realize()

        self.sizerHoriz = wx.BoxSizer(wx.HORIZONTAL)

        self.startButton = wx.Button(self, ID_STARTBUTTON, "Start")
        wx.EVT_BUTTON(self, ID_STARTBUTTON, self.pressStartButton);

        self.stopButton = wx.Button(self, ID_STOPBUTTON, "Stop")
        self.stopButton.Enable(0)
        wx.EVT_BUTTON(self, ID_STOPBUTTON, self.pressStopButton);

        self.clearButton = wx.Button(self, ID_CLEARBUTTON, "Clear")
        wx.EVT_BUTTON(self, ID_CLEARBUTTON, self.pressClearButton);

        configureButton = wx.Button(self, ID_CONFIGUREBUTTON, "Configure")
        wx.EVT_BUTTON(self, ID_CONFIGUREBUTTON, self.pressConfigureButton)

        self.sizerHoriz.Add(self.startButton, 0, wx.ALL, 5)
        self.sizerHoriz.Add(self.stopButton, 0, wx.ALL, 5)
        self.sizerHoriz.Add(self.clearButton, 0, wx.ALL, 5)
        self.sizerHoriz.Add(configureButton, 0, wx.ALL, 5)

        # Use some sizers to see layout options
        self.sizerVert = wx.BoxSizer(wx.VERTICAL)
        self.sizerVert.Add(self.sizerHoriz, 0, wx.EXPAND)
        self.sizerVert.Add(self.canvas, 1, wx.EXPAND)
        self.sizerVert.Add(self.toolbar, 0, wx.GROW)

        #Bind the update canvas event to some function:
        self.Bind(EVT_UPDATE_CANVAS, self.onUpdateCanvas)

        #Layout sizers
        self.SetSizer(self.sizerVert)
        self.SetAutoLayout(1)
        self.sizerVert.Fit(self)
        self.initialize()
        self.Show(1)

    def GetToolBar(self):
        return self.toolbar

    def pressStartButton(self, event):
        print "Starting background thread."
        self.startButton.Enable(0)
        self.configuration = self.prefwindow.getConfiguration()
        ezdaq.configure_device(self.configuration)
        ezdaq.configure_streaming(self.configuration)
        self.helper = helperThread(self)
        ezdaq.start_streaming()
        self.helper.start()
        self.stopButton.Enable(1)
        print "Start button complete."

    def pressStopButton(self, event):
        self.stopButton.Enable(0)
        self.helper.stopFlag = 0
        self.helper.join()
        ezdaq.stop_streaming()
        ezdaq.close_device()
        print "Background thread stopped."
        self.helper = None
        self.startButton.Enable(1)

    def pressClearButton(self, event):
        self.arrayLock.acquire()
        self.xarray = [X_INIT, Y_INIT]
        self.yarray = [Y_INIT, X_INIT]
        self.line.set_data(self.xarray, self.yarray)
        self.arrayLock.release()
        evt = UpdateCanvasEvent()
        wx.PostEvent(self, evt)

    def pressConfigureButton(self, event):
        self.prefwindow.Show(1)
        self.prefwindow.MakeModal(1)

    def initialize(self):
        self.subplot = self.figure.add_subplot(111)
        self.xarray = [X_INIT, Y_INIT]
        self.yarray = [Y_INIT, X_INIT]
        self.line, = self.subplot.plot(self.xarray, self.yarray, '.')
        self.toolbar.update()

    def onUpdateCanvas(self, evt):
        print "Updating the canvas..."
        self.canvasLock.acquire()
        self.canvas.draw()
        self.toolbar.update()
        self.canvasLock.release()

    def createPreferencesWindow(self):
        prefwindow = PreferencesFrame(self, "Preferences")
        prefwindow.CentreOnParent(wx.BOTH)
        return prefwindow

class helperThread(threading.Thread):
    def __init__(self, window):
        threading.Thread.__init__(self)
        self.window = window

    def run(self):
        print "Inside thread run method"
        self.stopFlag = 1

        while(self.stopFlag):
                try:
                    newlists = ezdaq.get_results()
                    if(newlists == None):
                        print "Waiting..."
                        #if exception is thrown, that means the buffer was empty and we have no results
                        #to post to the cavnas. While we're in here and otherwise waiting, lets trim the 
                        #list down if it gets to big
                        array_length = len(self.window.xarray)
                        if(array_length > ARRAY_LIM):
                            del self.window.xarray[0:array_length - ARRAY_LIM]
                            del self.window.yarray[0:array_length - ARRAY_LIM]
                        time.sleep(1) #change this to something smaller
                        continue
                except IOError:
                    print "Fatal Error"
                    break
                print "Got the data."
                newxlist = newlists[0]
                newylist = newlists[1]
                self.window.arrayLock.acquire()
                self.window.xarray.extend(newxlist)
                self.window.yarray.extend(newylist)
                self.window.line.set_data(self.window.xarray, self.window.yarray)
                self.window.arrayLock.release()
                evt = UpdateCanvasEvent()
                wx.PostEvent(self.window, evt)


class PreferencesFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, -1, title)

        #p = wx.Panel(self, -1)
        vertSizer = wx.BoxSizer(wx.VERTICAL)
        titleLabel = wx.StaticText(self, -1, "Comedi Configuration")
        okButton = wx.Button(self, ID_OKBUTTON, "Save Changes")
        wx.EVT_BUTTON(self, ID_OKBUTTON, self.PressOkButton)

        sizer = wx.GridSizer(6, 2, 2, 2) #rows, columns, hgap, vgap

        vertSizer.Add(titleLabel, 0, wx.ALIGN_CENTER | wx.ALL, 8)
        vertSizer.Add(sizer)
        vertSizer.Add(okButton, 0, wx.ALIGN_RIGHT | wx.ALL, 15)

        deviceNameLabel = wx.StaticText(self, -1, "Device: ")
        self.deviceNameTextBox = wx.TextCtrl(self, -1, "/dev/comedi0")
        sizer.Add(deviceNameLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.deviceNameTextBox)

        xInputLabel = wx.StaticText(self, -1, "X Input Channel: ")
        self.xInputBox = wx.Choice(self, -1, choices=['0', '1', '2', '3', '4', '5', '6', '7'])
        self.xInputBox.SetSelection(0)
        sizer.Add(xInputLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.xInputBox)

        xInputRangeLabel = wx.StaticText(self, -1, "X Input Range: ")
        self.xInputRangeBox = wx.Choice(self, -1, choices=['0', '1', '2', '3'])
        self.xInputRangeBox.SetSelection(1)
        sizer.Add(xInputRangeLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.xInputRangeBox)

        xInputTypeLabel = wx.StaticText(self, -1, "X Input Type: ")
        self.xInputTypeBox = wx.Choice(self, -1, choices=['AREF_GROUND', 'AREF_COMMON', 'AREF_DIFF'])
        self.xInputTypeBox.SetSelection(2)
        sizer.Add(xInputTypeLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.xInputTypeBox)

        yInputLabel = wx.StaticText(self, -1, "Y Input Channel: ")
        self.yInputBox = wx.Choice(self, -1, choices=['0', '1', '2', '3', '4', '5', '6', '7'])
        self.yInputBox.SetSelection(2)
        sizer.Add(yInputLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.yInputBox)

        yInputRangeLabel = wx.StaticText(self, -1, "Y Input Range: ")
        self.yInputRangeBox = wx.Choice(self, -1, choices=['0', '1', '2', '3'])
        self.yInputRangeBox.SetSelection(1)
        sizer.Add(yInputRangeLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.yInputRangeBox)

        yInputTypeLabel = wx.StaticText(self, -1, "Y Input Type: ")
        self.yInputTypeBox = wx.Choice(self, -1, choices=['AREF_GROUND', 'AREF_COMMON', 'AREF_DIFF'])
        self.yInputTypeBox.SetSelection(2)
        sizer.Add(yInputTypeLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.yInputTypeBox)

        inputPeriodLabel = wx.StaticText(self, -1, "Samples per second: ")
        self.inputPeriodBox = wx.TextCtrl(self, -1, "3000")
        sizer.Add(inputPeriodLabel, 0, wx.ALIGN_RIGHT)
        sizer.Add(self.inputPeriodBox)

        self.SetSizer(vertSizer)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.Fit()

    def PressOkButton(self, event):
        self.Close(1)

    def OnCloseWindow(self, event):
        self.MakeModal(False)
        self.Show(0)
        print self.getConfiguration()
        #self.Destroy()

    def getConfiguration(self):
        config = dict()
        config["device"] = self.deviceNameTextBox.GetValue()

        config["analog_period_ns"] = int(1e9 / float(self.inputPeriodBox.GetValue()))

        x_channel = int(self.xInputBox.GetStringSelection())
        x_range = int(self.xInputRangeBox.GetStringSelection())
        x_aref = self.xInputTypeBox.GetStringSelection()

        y_channel = int(self.yInputBox.GetStringSelection())
        y_range = int(self.yInputRangeBox.GetStringSelection())
        y_aref = self.yInputTypeBox.GetStringSelection()

        config["analog"] = [[x_channel, x_range, x_aref], [y_channel, y_range, y_aref]]
        return config

app = wx.PySimpleApp()
frame = MainWindow()
app.MainLoop()
