#  
# M.Mastrofini - Python
#  March 2017
#  example to read and draw a waveform saved on disk 
#  in binary format - bye or word 
#
from Tkinter import *
from tkFileDialog   import askopenfilename  
import numpy as np
import time
import os, sys, glob
import matplotlib
# the following 3 lines must be in that sequence - needed to embedd curve in Tk windows
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt

# default start directory
os.chdir("D:\\waveforms")

def makeWindow () :
    # 2 gloabla variable used in openfile()
    global TextMsg, fig, b1
    # call to tk()
    win = Tk()
    # x width, y hight; +x start, +y start - from upper left corner
    win.geometry("800x900+1000+50")
     
    frame1 = Frame(win, width=200, height =50)       # 2  buttons needed, select file and exit program
    frame1.pack()
    b1 = Button(frame1,text=" select TRC file ",command=OpenFile) # open file bitton
    b1.pack(side=LEFT)
    b2 = Button(frame1,text=" Exit Program ",command=win.quit)    # exit program button
    b2.pack(side=LEFT)
    # simple label for text message window
    frame2 = Frame(win, width=750, height =20)       
    frame2.pack()
    Label(frame2, text="Program Messages").grid(row=0, column=0, sticky=W)
    frame2.pack_propagate(0)
    # frame for actual text message window
    frame3 = Frame(win, width=750, height =300)       # select of Technologies
    frame3.pack()
    TextMsg = Text(frame3, height=4, width=150, wrap= WORD, state=NORMAL)
    # frame type with scroll  bar on the right
    scroll2 = Scrollbar(frame3, orient=VERTICAL)
    scroll2.pack(side=RIGHT, fill=Y)
    TextMsg.pack(side=LEFT, fill=Y)
    scroll2.config(command=TextMsg.yview)
    TextMsg.config(yscrollcommand=scroll2.set)
    TextMsg.insert(END, "please select the trace to draw \n")
    frame3.pack_propagate(0)
    # frame where plot will be shown
    frame4 = Frame(win, width=700)
    frame4.pack()
    # create the figure object and clear it to close the plot window outside Tk
    fig = plt.figure(1)
    fig.clf(1)
    #thes lines create the actual figure in Tk
    canvas = FigureCanvasTkAgg(fig, frame4)
    plot_widget = canvas.get_tk_widget()
    plot_widget.grid(row=0, column=0)
    # without this line will not work, why not clear
    return win
    
             # create text widget  for program messages
    
    
def OpenFile():
    # this function opens the file, set default directory, prints messages, downsamples with peak detect wfm and plots it
    # plot function has limited resolution (2 Mpoints I believe) in any case is better to reduce the actual drawn point
    sourcefile= askopenfilename() 
    sourcedir = os.path.dirname(sourcefile)
    # split name and path so message is shorter
    Dname, Fname = os.path.split(sourcefile)
    plt.title(Fname)
    filesize= os.path.getsize(sourcefile)
    os.chdir(sourcedir)
    msg =  "working Directory  " + Dname + " \n"
    b1.configure(fg="red", bg="gray")
    TextMsg.delete("1.0", END) 
    TextMsg.insert(END, msg)
    win.update()
    if (filesize >500) and ("trc"  in Fname):
        msg =  "trace selected is  " + Fname + "  " +  str(int(filesize/1e3)) + " KByte \n"
        TextMsg.insert(END, msg)
        win.update()
        data = open(sourcefile, "rb").read()
        #first 12 bye need to be discarded #9 and then number of bytes in the file
        data = data[11:]
        #start reading from header relevant parameter to draw the waveform
        Byte_or_Word = int(np.fromstring(data[32], dtype = "uint8"))
        Voffset = np.fromstring(data[160:164], dtype = "float32")[0]
        Vgain = np.fromstring(data[156:160], dtype = "float32")[0]
        Unit = chr(int(np.fromstring(data[196:197], dtype = "uint8")))
        # WDL WAVEDESC heder lenght usually 346 bytes 
        # np.fromstring returns nuple to make it a single value [0] is needed
        WDL = np.fromstring(data[36:40], dtype = "Int32")[0]
        RL = np.fromstring(data[60:64], dtype = "Int32")[0]
        NS = np.fromstring(data[174:176], dtype = "int16")[0]
        SampleInterval = np.fromstring(data[176:180], dtype = "float32")[0]
        Vmax = np.fromstring(data[164:168], dtype = "float32")[0]*(Vgain)+Vgain -Voffset
        Vmin = np.fromstring(data[168:172], dtype = "float32")[0]*Vgain -Voffset
        msg =  "red from file "+ str(RL) + " Bytes \n"
        msg = msg + "vertical gain = " + str(Vgain) + "\n"
        msg = msg + "vertical off set = " + str(Voffset) +"\n"
        msg = msg +  "Vertical unit is "+ str(Unit) + " \n"
        msg = msg + "WaveDesscription header is "+ str(WDL)+ " bytes long \n"
        msg = msg + "top grid level is "+ str(Vmax) +  Unit + ", "
        msg = msg + "bottom grid level is "+ str(Vmin) +  Unit + "\n"
        TextMsg.insert(END, msg)
        win.update()
        # VMax and VMin are returned as int16,  need to convert to float to determine vertical scale
        # remove first WDL bytes from data vector
        segments = int(len(data[WDL:])/512)
        dataword = data[WDL:]
              
        if segments <500:
            segments = 500
        if (Byte_or_Word == 0):
            rlen = (RL-2)
            points_in_segment=int(rlen/(segments))
            msg =  "Vertical scale is 8 bit  per sample"
            msg = msg +  ", "+ str(rlen) + " sample long \n"
            TimeWindow = rlen*SampleInterval
            msg= msg + "Trace is drawn using a "+ str(points_in_segment) + "x decimation factor \n"
            msg = msg + "Horizzontal time windows is " + str(TimeWindow) + "s \n"
            TextMsg.insert(END, msg)
            win.update()
            data =np.zeros(segments*2)
             #print points_in_segment, segments
            data =np.zeros(segments*2+1)
            # A better algorithm can be found to make sure we have a number of points_in_segment
            # which is an interg submultiple of the record length rlen
            # N pixels available, N/2 for max and N/2 for min
            for k in range(0,segments-1,1):
                subdata = dataword[(k)*points_in_segment:(k+1)*points_in_segment]
                #print (k)*points_in_segment, (k+1)*points_in_segment,k
                subdata =  np.fromstring(subdata, dtype=np.int8)   
            # in each segment search max and min value, and index of them in the vector
                Imax= np.argmax(subdata)
                Max = subdata[Imax]
                Imin= np.argmin(subdata)
                Min = subdata[Imin]
                #print (Max, Imax, Min, Imin, 2*k, 2*k+1, segments-k)
                
            # decide waht comes first in plot Min or Max
                if Imax > Imin:
                    data[2*k]= Min
                    data[2*k+1] =Max
                else:
                    data[2*k]= Max
                    data[2*k+1] = Min
            
        else:
            msg =  "Vertical scale is 16 bit  per sample"
            rlen = (RL-4)/2
            points_in_segment=int(rlen/(segments))
            msg = msg +  ", "+ str(rlen/1000) + " Ksample \n"
            msg= msg + "Trace is drawn using a "+ str(points_in_segment) + "x decimation factor \n"
            TimeWindow = rlen*SampleInterval
            msg = msg + "trace horizzontal time windows is " + str(TimeWindow) + "s \n"
            TextMsg.insert(END, msg)
            win.update()
            data =np.zeros(segments*2)
            # A better algorithm can be found to make sure we have a number of points_in_segment
            # which is an interg submultiple of the record length rlen
            # N pixels available, N/2 for max and N/2 for min
            for k in range(0,segments-1):
                #print k*points_in_segment, (k+2)*points_in_segment,k
                subdata = dataword[(k)*points_in_segment:(k+2)*points_in_segment]
                subdata =  np.fromstring(subdata, dtype=np.int16) 
                #print len(subdata)  
            # in each segment search max and min value, and index of them in the vector
                Imax= np.argmax(subdata)
                Max = subdata[Imax]
                Imin= np.argmin(subdata)
                Min = subdata[Imin]
                #print (Max, Imax, Min, Imin, 2*k, 2*k+1, segments-k)
            # decide waht comes first in plot Min or Max
                if Imax > Imin:
                    data[2*k]= Min
                    data[2*k+1] =Max
                else:
                    data[2*k]= Max
                    data[2*k+1] = Min
            
        if (NS == 1):
            msg = "this is not a segmented wfm \n"
            TextMsg.insert(END, msg)
            win.update()
        else:
            msg = "this waveform has " + str(NS)+ " segments " + "this example program can't manage segmented waveforms"
            TextMsg.insert(END, msg)
            win.update()
            win.quit()
     
        
        T0=-SampleInterval * rlen/2
        msg =  "axix Limits:  X left = "+ str(T0)+ "s"
        T1=SampleInterval * rlen/2
        msg =  msg + " -  X right  = " + str(T1) + "s"
        TextMsg.insert(END, msg)
        win.update()
        # need to calculate x axis horizontal  time steps to create x axis vector
        # hstep is close to SampleInterval, but not identical
        hstep=(T1-T0)/len(data)
        #print (T1-T0)/hstep, 
        #X axix vector can be created now
        x=np.arange(T0, T1, hstep)
        # randomly x()  is 1 point longer than data() probably because of the way np.arange(T0, T1, hstep) is constructed
        # control on length of these 2 vector is required to avoid error message
        if len(x)>len(data):
            x = x[0:len(x)-1]
        #print len(x), len(data), T1-T0, hstep,
        #converting now int16 to float
        data = (data * Vgain) - Voffset
        #Imax= np.argmax(data)
        #Max = data[Imax]
        #Imin= np.argmin(data)
        #Min = data[Imin]
        #define Plot tile,  axis, limits
        #matplotlib.use('TkAgg')
        
        plt.ylabel(Unit)
        plt.xlabel("time in seconds")
        #print Vmin - abs(0.1*Vmin), Vmax + abs(0.1*Vmax)
        plt.ylim(Vmin - abs(0.1*Vmin), Vmax + abs(0.1*Vmax))
        plt.xlim(T0, T1)
        #pOltting data
        plt.plot(x,data)
        #fig.canvas.draw()
        plt.ion()
        #plt.show()
        fig.canvas.draw()
        
        
        plt.ioff()
    else:               
        msg= " invalid TRC file "+ Fname + "\n"
        b1.configure(fg="red", bg="gray")
        sourcefile = " "
        #selectfile.configure(fg="red")
        #TextMsg.delete("1.0", END) 
        TextMsg.insert(END, msg)
    b1.configure(fg="red", bg="gray")

            



win = makeWindow()
#master.title("Select trace you want to draw")



win.mainloop()
plt.clf()
win.destroy()

sys.exit()
