# Python/Tkinter GUI for MBARI pCO2/DIC controller
# [a first exercise in Python]


# import all components
# from the tkinter library
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from cffi import *
import pathlib
import os
import sys
import re

import datetime as dt
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
import threading, time

# import filedialog module
from tkinter import filedialog

# Function for opening the
# file explorer root
def browseFiles(dir_name, label):

    filename = filedialog.askopenfilename(initialdir = dir_name,
                                          title = "Select a File",
                                          filetypes = (("all files", "*.*"),
                                                       ("Text files","*.txt"),
                                                       ("Script files","*.scr")),
                                          )

    # Change label contents
    label.delete(0,END)
    label.insert(0,filename)
    return;

def browseDevices(dir_name, cbox):
    print("browseDev:",dir_name)
    filename = filedialog.askopenfilename(initialdir = dir_name,
                                          title = "Select a File",
                                          filetypes = (("all files", "*.*"),
                                                       ("COM files","COM*"),
                                                       ("tty","tty.*"),
                                                       ("raw tty","cu.*")),
                                          )

    # add to cbox
    if filename not in cbox_ser_device['values']:
        cbox_ser_device['values'] = (*cbox_ser_device['values'],filename)
    cbox_ser_device.set(filename)

    return;

def swapFiles(src, dest):
 tmp = src.get()
 src.delete(0,END)
 src.insert(0,dest.get())
 dest.delete(0,END)
 dest.insert(0,tmp)
 return;

def validateCWGFile(dest):
    retval = 1
    err = 0
    dot = dest.find(".")
    elen = len(dest[dot:])-1
    print( "validating:"+dest )
    print( "len:",len(dest) )
    print( "dot:", dot, "elen:",elen )
    if len(dest) > 12 or len(dest) <= 0:
        msg = b"invalid len ["+str(len(dest)).encode()+b"]"
        messagebox.showwarning(title = "Invalid Dest", message = msg)
        retval = 0
        err = 1
    elif dot > 8 or (dot >= 0 and elen > 3 ):
        messagebox.showwarning(title = "Invalid Dest", message = "8.3 format required")
        retval = 0
        err = 2

    print("valDes - ret",retval, "err:", err)
    return retval

def validateHostFile(src, testIsFile):
    retval = 1
    err = 0
    if len(src) <= 0:
        msg = b"len<=0 or len>12 ["+str(len(src)).encode()+b"]"
        messagebox.showwarning(title = "Invalid Src", message = msg)
        retval = 0
        err = 1
    elif testIsFile and not os.path.isfile(src):
        msg = b"not a file ["+src.encode()+b"]"
        messagebox.showwarning(title = "Invalid Src", message = msg)
        retval = 0
        err = 2

    print("valSrc - ret",retval, "err:", err)
    return retval

def validateDev(dev):
    retval = 1
    err = 0
    if len(dev) <= 0:
        msg = b"invalid len ["+str(len(dev)).encode()+b"]"
        messagebox.showwarning(title = "Invalid Dev", message = msg)
        retval = 0
        err = 1
    elif os.path.isfile(dev) or os.path.isdir(dev):
        msg = b"is file or dir ["+str(len(dev)).encode()+b"]"
        messagebox.showwarning(title = "Invalid Dev", message = msg)
        retval = 0
        err = 2

    print("valDev - ret",retval, "err:", err)
    return retval

def doUpload():
    print("uploading for real (not really)")
    test = cwg.cwg_ser_connect(ser,int(9600),cwg.CSZ_8,cwg.PAR_N,cwg.CSB_1,cwg.FLOW_N)
    print("connect:",test)
    cwg.cwg_ser_show(ser,1,int(5))
    mode = ffi.cast('bool',mode_sel_up.get())
    print("mode>>>",mode)
    test = cwg.cwg_upload_file(ser,cbox_up_dest.get().encode(),ent_up_src.get().encode(),mode_sel_up.get())
#    test = cwg.cwg_write_delay(ser,b"\r\n",2,2)
#    print("wr:",test)
    test = cwg.cwg_ser_disconnect(ser)
    print("disconnect:",test)
    return 0

def doDownload():
    print("downloading for real (not really)")
    test = cwg.cwg_ser_connect(ser,int(9600),cwg.CSZ_8,cwg.PAR_N,cwg.CSB_1,cwg.FLOW_N)
    print("connect:",test)
    cwg.cwg_ser_show(ser,1,int(5))
    mode = ffi.cast('bool',mode_sel_down.get())
    print("mode>>>",mode)
    test = cwg.cwg_download_file(ser,ent_down_dest.get().encode(),cbox_down_src.get().encode(),mode_sel_down.get())
    #    test = cwg.cwg_write_delay(ser,b"\r\n",2,2)
    #    print("wr:",test)
    test = cwg.cwg_ser_disconnect(ser)
    print("disconnect:",test)
    return 0

def validateUpload():
    info = "src:"+ent_up_src.get()+"\n"
    info +="dest:"+cbox_up_dest.get()+"\n"
    info +="port:"+cbox_ser_device.get()+"\n"
    info +="mode:"+str(mode_sel_up.get())+"\n"
    # get python string from cdata char*
    dstr = ffi.string(ser.device)
    print(ent_up_src.get())
    print("cur "+dstr.decode())
    # use encode to convert from str to butes (cdata char*)
    cwg.cwg_ser_set_device(ser,cbox_ser_device.get().encode())
    dstr = ffi.string(ser.device)
    print("new "+dstr.decode())
    print("mode ",mode_sel_up.get())
    # use decode method to convert from bytes to str
#    info += "ser:"+dstr.decode()+"\n"
    val = 1
    if validateCWGFile(cbox_up_dest.get()) == 0 :
        val = 0
    if validateHostFile(ent_up_src.get() , True) == 0 :
        val = 0
    if validateDev(cbox_ser_device.get()) == 0 :
        val = 0
    if val and messagebox.askokcancel(title = "upload", message = info):
        doUpload()
    return

def validateDownload():
    info = "src:"+cbox_down_src.get()+"\n"
    info +="dest:"+ent_down_dest.get()+"\n"
    info +="port:"+cbox_ser_device.get()+"\n"
    # get python string from cdata char*
    dstr = ffi.string(ser.device)
    print(cbox_down_src.get())
    print("cur "+dstr.decode())
    # use encode to convert from str to butes (cdata char*)
    cwg.cwg_ser_set_device(ser,cbox_ser_device.get().encode())
    dstr = ffi.string(ser.device)
    print("new "+dstr.decode())
    # use decode method to convert from bytes to str
    #    info += "ser:"+dstr.decode()+"\n"
    val = 1
    if validateCWGFile(cbox_down_src.get()) == 0 :
        val = 0
    if validateHostFile(ent_down_dest.get(), False) == 0 :
        val = 0
    if validateDev(cbox_ser_device.get()) == 0 :
        val = 0
    if val and messagebox.askokcancel(title = "download", message = info):
        doDownload()
    return

def devSelected(event):
    event.widget.selection_clear()
    return

def devRefresh():
    cbox_ser_device['values']=()
    for f_name in os.listdir(device_dir):
        if f_name not in cbox_ser_device['values']:
            if sys.platform.startswith("darwin"):
                if f_name.startswith("cu.") or f_name.startswith("tty.") \
                or f_name.startswith("usb") :
                    cbox_ser_device['values'] = (*cbox_ser_device['values'],device_dir / f_name)
            elif sys.platform.startswith("cygwin"):
                if f_name.startswith("cu") or f_name.startswith("tty") \
                or f_name.startswith("usb") :
                    cbox_ser_device['values'] = (*cbox_ser_device['values'],device_dir / f_name)
                pass
            elif sys.platform.startswith("win32"):
                ports = ['COM%s' % (i + 1) for i in range(256)]
                pass

    cbox_ser_device.current(0)

    return

def startStripChart():
    global plot_running
    global plot_period
    global plot_en
    global xs
    global ys
    print("starting strip chart (connecting)",cbox_ser_device.get())
    cwg.cwg_ser_set_device(ser,cbox_ser_device.get().encode())
    test = cwg.cwg_ser_connect(ser,int(9600),cwg.CSZ_8,cwg.PAR_N,cwg.CSB_1,cwg.FLOW_N)
    print("connect:",test)
    cwg.cwg_ser_show(ser,1,int(5))

    plot_period=float(ent_plot_period.get())
    if plot_period < 0.5:
        plot_period=1.0
    echo_dis="set var echo 1\r\n"
    prompt_dis="set var prompt 1\r\n"
    mdio="mdio +li\r\n"
    mmeas="mmeas tm:air,fo,p"+'{:.1f}'.format(plot_period)+",t0\r\n"
    py_lbuf = ffi.new("char[]",256)
    print(echo_dis," ",len(echo_dis))
    print(prompt_dis," ",len(prompt_dis))
    print(mdio," ",len(mdio))
    print(mmeas," ",len(mmeas))

    cwg.cwg_write_delay(ser,echo_dis.encode(),ffi.cast("size_t",len(echo_dis)),200)
    cwg.cwg_write_delay(ser,prompt_dis.encode(),ffi.cast("size_t",len(prompt_dis)),200)
    cwg.cwg_write_delay(ser,mdio.encode(),ffi.cast("size_t",len(mdio)),200)
    cwg.cwg_write_delay(ser,mmeas.encode(),ffi.cast("size_t",len(mmeas)),200)
    cwg.cwg_wdrain_rx(ser,5)
    plot_running=True
    updateStripChart()
    print("startStripChart DONE")
    return

def stopStripChart():
    global plot_updating
    global plot_running
    global plot_en
    print("stopping strip chart ",cbox_ser_device.get())
    cwg.cwg_ser_set_device(ser,cbox_ser_device.get().encode())
    while(plot_updating):
        pass
    plot_running=False
    plot_en=False
    ctrl_d="\x04\x04\x04\r\n"
    cwg.cwg_write_delay(ser,ctrl_d.encode(),len(ctrl_d),200)
    mdio="mdio -li\r\n"
    cwg.cwg_write_delay(ser,mdio.encode(),len(mdio),200)
    cwg.cwg_wdrain_rx(ser,5)
    test = cwg.cwg_ser_disconnect(ser)
    return

def updateStripChart():
    global plot_running
    global plot_updating
    global plot_en
    global plot_period
    global xs
    global ys
    if plot_en and plot_running:
        plot_updating=True
        print("updating strip chart")
        py_lbuf = ffi.new("char[]",256)
        test= False #True

        rval=0
        if not test:
            rval = cwg.cwg_read_until(ser,b"\r\n",py_lbuf,256,2)
        print("rval:",rval," [",ffi.string(py_lbuf),"/",len(ffi.string(py_lbuf)),"]")
        if test or rval > 0 and len(ffi.string(py_lbuf)) > 0:
            # parse the string
            if not test :
                dlist = ffi.string(py_lbuf).decode().split(",")
            else:
                # fake code for testing
                yval=10*random.random()
                dstr=dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')+","+'{:e}'.format(yval)
                dlist = dstr.split(",")
            print(dlist)
            tstamp=dlist[0]
            try:
                xs.append(dt.datetime.fromisoformat(tstamp[0:19]))
                if test:
                    idx=1
                else:
                    idx=5
                ys.append(float(dlist[int(idx)]))
                print("x,y[",idx,"]:",tstamp[0:19],",",float(dlist[int(idx)]))

                # Limit x and y lists to 20 items
                depth=int(ent_plot_depth.get())
                if depth < 0 or depth > 1000:
                    depth=-20
                else:
                    depth*=-1
                xs = xs[int(depth):]
                ys = ys[int(depth):]

                # Draw x and y lists
                ax[0].clear()
                ax[0].plot(xs, ys)

                # Format plot
                plt.xticks(rotation=45, ha='right')
                plt.subplots_adjust(bottom=0.10)
#                plt.title('LI-COR CO2')
#                plt.ylabel('CO2 concentration (some units)')
                ax[0].set_title('LI-COR CO2')
                ax[0].set_ylabel('CO2 concentration (some units)')
                plt.draw()
            except Exception as e:
                print("exception:",e)
                rval = cwg.cwg_read_until(ser,b"\r\n",py_lbuf,256,4)

            plot_updating=False

        if plot_en and plot_running:
            period_ms=int(plot_period*1000)
            if period_ms < 500:
                period_ms = int(500)
            btn_plot_enable.after(period_ms,updateStripChart)

    else:
        print("strip chart not running")


    return


def licorPowerOn():
    cwg.cwg_ser_set_device(ser,cbox_ser_device.get().encode())
    test = cwg.cwg_ser_connect(ser,int(9600),cwg.CSZ_8,cwg.PAR_N,cwg.CSB_1,cwg.FLOW_N)
    mdio="mdio +li\r\n"
    cwg.cwg_write_delay(ser,mdio.encode(),len(mdio),200)
    cwg.cwg_wdrain_rx(ser,5)
    test = cwg.cwg_ser_disconnect(ser)
    return

def licorPowerOff():
    cwg.cwg_ser_set_device(ser,cbox_ser_device.get().encode())
    test = cwg.cwg_ser_connect(ser,int(9600),cwg.CSZ_8,cwg.PAR_N,cwg.CSB_1,cwg.FLOW_N)
    mdio="mdio -li\r\n"
    cwg.cwg_write_delay(ser,mdio.encode(),len(mdio),200)
    cwg.cwg_wdrain_rx(ser,5)
    test = cwg.cwg_ser_disconnect(ser)
    return

def togglePlotStop():
    global plot_en
    if plot_en:
        plot_en=False
        stopStripChart()
        btxt_plot_enable.set("Start")
        plot_running=False
        plot_updating=False
    else:
        plot_en=True
        btxt_plot_enable.set("Stop")
        startStripChart()
    return

def toggleLicorPower():
    global licor_power
    if licor_power:
        licorPowerOff()
        licor_power=False
        btxt_licor_power.set("On")
    else:
        licorPowerOn()
        licor_power=True
        btxt_licor_power.set("Off")
    return

def cwgDir(cbox):
    cwg.cwg_ser_set_device(ser,cbox_ser_device.get().encode())
    test = cwg.cwg_ser_connect(ser,int(9600),cwg.CSZ_8,cwg.PAR_N,cwg.CSB_1,cwg.FLOW_N)
    cmd_dir="dir q\r\n"
    cwg.cwg_write_delay(ser,cmd_dir.encode(),len(cmd_dir),200)
    py_lbuf = ffi.new("char[]",256)
    test= False #True
    rval=0
    flist = list()
    if not test:
        flist = list()
        while True:
            rval = cwg.cwg_read_until(ser,b"\r\n",py_lbuf,256,2)
            if rval <=0:
                break
#            print("rval:",rval," [",ffi.string(py_lbuf),"/",len(ffi.string(py_lbuf)),"]")
#            print("line:",ffi.string(py_lbuf).decode())
            ftoks=ffi.string(py_lbuf).decode().split("\r\n")
#            print("ftoks:",ftoks,"/",len(ftoks))
            if len(ftoks) > 1:
                flist.append(ftoks[0])
    cwg.cwg_wdrain_rx(ser,5)
    test = cwg.cwg_ser_disconnect(ser)
#    print("flist:",flist)
    # TODO: populate combobox
    if not cbox is None:
        cbox['values'] = ()
        for f_name in flist[0:] :
            if f_name not in cbox['values']:
                cbox['values'] = (*cbox['values'],f_name)
        cbox.current(0)
    return

# This function is called periodically from FuncAnimation
#def animate(i, xs, ys):
#
#    # y-value
#    yval = 10*random.random()
#
#    # Add x and y to lists
#    xs.append(dt.datetime.now().strftime('%H:%M:%S.%f'))
#    ys.append(yval)
#
#    # Limit x and y lists to 20 items
#
#    xs = xs[-20:]
#    ys = ys[-20:]
#
#    # Draw x and y lists
#    ax.clear()
#    ax.plot(xs, ys)
#
#    # Format plot
#    plt.xticks(rotation=45, ha='right')
#    plt.subplots_adjust(bottom=0.30)
#    plt.title('Random numbers')
#    plt.ylabel('Who knows?')
#    plt.draw()
#    return

def shutdown():
    try:
        print("stopping strip chart...")
        stopStripChart()
        print("disconnecting...")
        cwg.cwg_ser_disconnect(ser)
#        print(ser)
#        print(ffi.typeof(ser))
        print("releasing resources...")
        pser =ffi.new("cwg_ser_t **",ser)
#        print(pser)
        cwg.cwg_ser_destroy(pser)
    except Exception as e:
        print("shutdown error: ",e)
    print("done")
    exit()

# ////////////////
# Main Entry Point

####################
# Root window config
root = Tk()

# Set root title
root.title('CWG File Transfer')

# Set root size
root.geometry("550x350")

#Set root background color
root.config(background = "gainsboro")
root.columnconfigure(0, weight = 1)
root.rowconfigure(0, weight = 1)

if sys.platform.startswith("darwin"):
    device_dfl = "/dev/null"
    cwd_dir = pathlib.Path().cwd()
    device_dir = pathlib.Path('/dev')
    style_dfl="aqua"
elif sys.platform.startswith("cygwin"):
    device_dfl = "/dev/null"
    cwd_dir = pathlib.Path().cwd()
    device_dir = pathlib.Path('/dev')
    style_dfl="classic"
elif sys.platform.startswith("win32"):
    print("win32 support pending")
    style_dfl="winnative"
else :
    print("unsupported OS: ",sys.platform)


####################
# Control styles
""" themes: classic default clam alt
    OSX: aqua
    WIN: vista winnative xpnative
    """
style = ttk.Style()
style.theme_use(style_dfl)
style.configure("BlkGains.TLabel", foreground = "black", background = "gainsboro")
style.configure("BlkGains.TFrame", foreground = "black", background = "gainsboro")
style.configure("RedGains.TLabel", foreground = "red", background = "gainsboro")
style.configure("GrnWht.TLabel", foreground = "green", background = "white")
style.configure("GrnGry.TCombobox", foreground = "green", background = "gray")
style.configure("BluGry.TCombobox", foreground = "blue", background = "gray")
style.configure("BlkGry.TCombobox", foreground = "black", background = "gray")
style.configure("RedGry.TCombobox", foreground = "red", background = "gray")
style.configure("RedWht.TLabel", foreground = "red", background = "white")
style.configure("GrnWht.TLabel", foreground = "green", background = "white")
style.configure("BluWht.TButton", foreground = "blue", background = "white")
style.configure("GrnWht.TButton", foreground = "green", background = "white")
style.configure("RedWht.TButton", foreground = "red", background = "white")
style.configure("BluWht.TEntry", foreground = "blue", background = "white")
style.configure("BlkWht.TEntry", foreground = "black", background = "white")
style.configure("GrnWht.TEntry", foreground = "green", background = "white")
style.configure("RedWht.TEntry", foreground = "red", background = "white")

####################
# Notebook and frames

notebook=ttk.Notebook(root)

frm_nav = ttk.Frame(root, padding = "2 2 10 10", style="BlkGains.TFrame")

frm_upload = ttk.Frame(notebook, padding = "2 2 10 10")
frm_rb_up = ttk.Frame(frm_upload, padding = "2 2 10 10")
frm_upaction = ttk.Frame(frm_upload, width = 100, padding = "2 2 15 15")

frm_download = ttk.Frame(notebook, padding = "2 2 10 10")
frm_rb_down = ttk.Frame(frm_download, padding = "2 2 10 10")
frm_down_action = ttk.Frame(frm_download, width = 100, padding = "2 2 15 15")

frm_settings = ttk.Frame(notebook, padding = "2 2 10 10")
frm_plot = ttk.Frame(notebook, padding = "2 2 10 10")
frm_licor = ttk.Frame(notebook, padding = "2 2 10 10")

notebook.add(frm_settings, text = "Settings")
notebook.add(frm_upload, text = "Host > CWG")
notebook.add(frm_download, text = "CWG > Host")
notebook.add(frm_plot, text = "Plot")
notebook.add(frm_licor, text = "LI-COR")

####################
# Settings controls

btn_ser_device = ttk.Button(frm_settings,
                        text = "Device",
                        width = 10,
                        padding = "2 2 10 10",
                        command = lambda: browseDevices(device_dir, cbox_ser_device ) )
cbox_ser_device = ttk.Combobox(frm_settings,
                               style = "BlkGry.TCombobox",
                               width = 35)
cbox_ser_device.bind("<<ComboboxSelected>>", devSelected)

btn_ser_refresh = ttk.Button(frm_settings,
                            text = "Refresh",
                            width = 10,
                            padding = "2 2 10 10",
                            command = lambda: devRefresh() )

####################
# Upload controls

ent_up_src = ttk.Entry(frm_upload,
                       width = 35,
                       style = "GrnWht.TEntry")
ent_up_src.insert(0,"")

btn_up_src = ttk.Button(frm_upload,
                        text = "host",
                        width = 10,
                        padding = "2 2 10 10",
                        command = lambda: browseFiles(cwd_dir, ent_up_src ) )

cbox_up_dest = ttk.Combobox(frm_upload,
                             style = "RedGry.TCombobox",
                             width = 35)
cbox_up_dest.bind("<<ComboboxSelected>>", devSelected)

btn_up_dest = ttk.Button(frm_upload,
                          text = "cwg",
                          width = 10,
                          padding = "2 2 10 10",
                          command = lambda: cwgDir(cbox_up_dest) )

mode_txt = BooleanVar()
mode_txt = False
mode_bin = BooleanVar()
mode_bin = True
mode_txt_str = "Text"
mode_bin_str = "Binary"
mode_sel_up = BooleanVar()
mode_sel_up.set(mode_txt)
rb_up_mode_txt = ttk.Radiobutton(frm_rb_up,
                    text = mode_txt_str,
                    variable = mode_sel_up,
                    value = mode_txt
                    )
rb_up_mode_bin = ttk.Radiobutton(frm_rb_up,
                             text = mode_bin_str,
                             variable = mode_sel_up,
                             value = mode_bin,
                             )

btn_up_swap = ttk.Button(frm_upload,
                         text = "Swap Src/Des",
                      command = lambda: swapFiles(ent_up_src, cbox_up_dest ) )

btn_up_upload = ttk.Button(frm_upaction,
                         text = "Upload",
                       command = lambda: validateUpload() )

####################
# Download controls

ent_down_dest = ttk.Entry(frm_download,
                       width = 35,
                       style = "RedWht.TEntry")

ent_down_dest.insert(0,"")

btn_down_dest = ttk.Button(frm_download,
                           text = "host",
                           width = 10,
                           padding = "2 2 10 10",
                           command = lambda: browseFiles(cwd_dir, ent_down_dest ) )

cbox_down_src = ttk.Combobox(frm_download,
                             style = "GrnGry.TCombobox",
                             width = 35)
cbox_down_src.bind("<<ComboboxSelected>>", devSelected)

btn_down_src = ttk.Button(frm_download,
                          text = "cwg",
                          width = 10,
                          padding = "2 2 10 10",
                          command = lambda: cwgDir(cbox_down_src) )

mode_sel_down = BooleanVar()
mode_sel_down.set(mode_txt)
rb_down_mode_txt = ttk.Radiobutton(frm_rb_down,
                                 text = mode_txt_str,
                                 variable = mode_sel_down,
                                 value = mode_txt
                                 )
rb_down_mode_bin = ttk.Radiobutton(frm_rb_down,
                                 text = mode_bin_str,
                                 variable = mode_sel_down,
                                 value = mode_bin,
                                 )

btn_down_swap = ttk.Button(frm_download,
                      text = "Swap Src/Des",
                      command = lambda: swapFiles(cbox_down_src, ent_down_dest ) )

btn_down_download = ttk.Button(frm_down_action,
                    text = "Download",
                    command = lambda: validateDownload() )


####################
# Navigation controls
btn_quit = ttk.Button(frm_nav,
                         text = "Quit",
                         command = shutdown)

####################
# Plot controls
# Create figure for plotting
fig = plt.figure(num="LI-COR")

ax=list()
i=1;
while i <= 4:
    if i == 1:
        ax.append(fig.add_subplot(4,1,i) )
    else:
        ax.append(fig.add_subplot(4,1,i,sharex=ax[0]) )
    ax[i-1].xaxis.set_visible(False)
    i+=1

ax[3].xaxis.set_visible(True)
#ax.append(fig.add_subplot(1,1,1))
#ax[0] = fig.add_subplot(7,2,1)
#ax[1] = fig.add_subplot(7,2,2,sharex=ax, ymargin=5.0)
#ax[2] = fig.add_subplot(7,2,5,sharex=ax, ymargin=5.0)

xs = []
ys = []

# Initialize random number generator
random.seed()

# Set up plot 
pcount=0

plot_en=False
plot_running=False
plot_updating=False
plot_buffer_depth=20
plot_period=1.0

btxt_plot_enable=StringVar()
btxt_plot_enable.set("Start")
btn_plot_enable = ttk.Button(frm_plot,
                             textvariable = btxt_plot_enable,
                             command = lambda: togglePlotStop() )


plt.ioff()
plt.show(block=False)

ent_plot_depth = ttk.Entry(frm_plot,
                         width = 5,
                         justify = "right",
                         style = "BlkWht.TEntry")
ent_plot_depth.insert(0,"20")
lbl_plot_depth = ttk.Label(frm_plot,
                         text = "points",
                         style = "BlkGains.TLabel",
                         width = 8,
                         padding = "2 2 10 10",
                         anchor = "center",
                         relief = "sunken")

ent_plot_period = ttk.Entry(frm_plot,
                           width = 5,
                            justify = "right",
                           style = "BlkWht.TEntry")
ent_plot_period.insert(0,str(plot_period))
lbl_plot_period = ttk.Label(frm_plot,
                           text = "period",
                           style = "BlkGains.TLabel",
                           width = 8,
                           padding = "2 2 10 10",
                           anchor = "center",
                           relief = "sunken")

####################
# LI-COR controls

licor_power=False
btxt_licor_power=StringVar()
btxt_licor_power.set("On")
btn_licor_power = ttk.Button(frm_licor,
                             textvariable = btxt_licor_power,
                             command = lambda: toggleLicorPower() )
lbl_licor_power = ttk.Label(frm_licor,
                           text = "power",
                           style = "BlkGains.TLabel",
                           width = 8,
                           padding = "2 2 10 10",
                           anchor = "center",
                           relief = "sunken")

####################
# Layout

# root frame contents
notebook.grid(column = 0, row = 0, sticky = (N, W, E, S))
frm_nav.grid(column = 0, row = 1)

# notebook contents
#frm_upload.grid(column = 0, row = 0)

# frm_nav contents
btn_quit.grid(column = 2, row = 0)

# frm_settings contents
btn_ser_device.grid(column = 1, row = 1, sticky = (W,E))
btn_ser_refresh.grid(column = 1, row = 2, sticky = (W,E))
cbox_ser_device.grid(column = 2, row = 1)

# frm_upload contents
frm_upaction.grid(column = 2, row = 7)
frm_rb_up.grid(column = 1, row = 4, sticky = (W, E))
btn_up_src.grid(column = 1, row = 2, sticky = (W,E))
ent_up_src.grid(column = 2, row = 2)
btn_up_dest.grid(column = 1, row = 3, sticky = (W,E))
cbox_up_dest.grid(column = 2, row = 3)
btn_up_swap.grid(column = 1, row = 5, sticky = (W,E))
btn_up_upload.grid(column = 0,row = 0 )

# frm_rb_up contents
rb_up_mode_txt.grid(column = 1, row = 0, sticky = (W,E))
rb_up_mode_bin.grid(column = 1, row = 1, sticky = (W,E))

# frm_download contents
frm_down_action.grid(column = 2, row = 7)
frm_rb_down.grid(column = 1, row = 4, sticky = (W, E))
btn_down_dest.grid(column = 1, row = 3, sticky = (W,E))
ent_down_dest.grid(column = 2, row = 3)
btn_down_swap.grid(column = 1, row = 5, sticky = (W,E))
btn_down_download.grid(column = 0,row = 0 )
btn_down_src.grid(column = 1,row = 2, sticky = (W,E) )
cbox_down_src.grid(column = 2,row = 2 )
btn_down_swap.grid(column = 1, row = 5, sticky = (W,E))

# frm_rb_down contents
rb_down_mode_txt.grid(column = 1, row = 0, sticky = (W,E))
rb_down_mode_bin.grid(column = 1, row = 1, sticky = (W,E))

# frm_plot contents
lbl_plot_depth.grid(column = 1, row = 1, sticky = (W,E))
ent_plot_depth.grid(column = 2, row = 1)
lbl_plot_period.grid(column = 1, row = 2, sticky = (W,E))
ent_plot_period.grid(column = 2, row = 2)
btn_plot_enable.grid(column = 1, row = 3)

# frm_licor contents
lbl_licor_power.grid(column = 1, row = 1)
btn_licor_power.grid(column = 2, row = 1)

####################
# Bind C libraries

ffi = FFI()

ffi.cdef("""

    #define TCGETS        0x5401
    #define TCSETS        0x5402
    #define TCSETSW        0x5403
    #define TCSETSF        0x5404
    #define TCGETA        0x5405
    #define TCSETA        0x5406
    #define TCSETAW        0x5407
    #define TCSETAF        0x5408
    #define TCSBRK        0x5409
    #define TCXONC        0x540A
    #define TCFLSH        0x540B
    #define TIOCEXCL    0x540C
    #define TIOCNXCL    0x540D
    #define TIOCSCTTY    0x540E
    #define TIOCGPGRP    0x540F
    #define TIOCSPGRP    0x5410
    #define TIOCOUTQ    0x5411
    #define TIOCSTI        0x5412
    #define TIOCGWINSZ    0x5413
    #define TIOCSWINSZ    0x5414
    #define TIOCMGET    0x5415
    #define TIOCMBIS    0x5416
    #define TIOCMBIC    0x5417
    #define TIOCMSET    0x5418
    #define TIOCGSOFTCAR    0x5419
    #define TIOCSSOFTCAR    0x541A

    #define NCCS 17
    struct termios {
    unsigned long c_iflag;        /* input mode flags */
    unsigned long c_oflag;        /* output mode flags */
    unsigned long c_cflag;        /* control mode flags */
    unsigned long c_lflag;        /* local mode flags */
    unsigned char c_line;        /* line discipline */
    unsigned char c_cc[NCCS];    /* control characters */
    };
    /* c_cc characters */
    #define VINTR 0
    #define VQUIT 1
    #define VERASE 2
    #define VKILL 3
    #define VEOF 4
    #define VTIME 5
    #define VMIN 6
    #define VSWTC 7
    #define VSTART 8
    #define VSTOP 9
    #define VSUSP 10
    #define VEOL 11
    #define VREPRINT 12
    #define VDISCARD 13
    #define VWERASE 14
    #define VLNEXT 15
    #define VEOL2 16
    /* c_iflag bits */
    #define IGNBRK    0000001
    #define BRKINT    0000002
    #define IGNPAR    0000004
    #define PARMRK    0000010
    #define INPCK    0000020
    #define ISTRIP    0000040
    #define INLCR    0000100
    #define IGNCR    0000200
    #define ICRNL    0000400
    #define IUCLC    0001000
    #define IXON    0002000
    #define IXANY    0004000
    #define IXOFF    0010000
    #define IMAXBEL    0020000
    /* c_oflag bits */
    #define OPOST    0000001
    #define OLCUC    0000002
    #define ONLCR    0000004
    #define OCRNL    0000010
    #define ONOCR    0000020
    #define ONLRET    0000040
    #define OFILL    0000100
    #define OFDEL    0000200
    #define NLDLY    0000400
    #define   NL0    0000000
    #define   NL1    0000400
    #define CRDLY    0003000
    #define   CR0    0000000
    #define   CR1    0001000
    #define   CR2    0002000
    #define   CR3    0003000
    #define TABDLY    0014000
    #define   TAB0    0000000
    #define   TAB1    0004000
    #define   TAB2    0010000
    #define   TAB3    0014000
    #define   XTABS    0014000
    #define BSDLY    0020000
    #define   BS0    0000000
    #define   BS1    0020000
    #define VTDLY    0040000
    #define   VT0    0000000
    #define   VT1    0040000
    #define FFDLY    0040000
    #define   FF0    0000000
    #define   FF1    0040000
    /* c_cflag bit meaning */
    #define CBAUD    0000017
    #define  B0    0000000        /* hang up */
    #define  B50    0000001
    #define  B75    0000002
    #define  B110    0000003
    #define  B134    0000004
    #define  B150    0000005
    #define  B200    0000006
    #define  B300    0000007
    #define  B600    0000010
    #define  B1200    0000011
    #define  B1800    0000012
    #define  B2400    0000013
    #define  B4800    0000014
    #define  B9600    0000015
    #define  B19200    0000016
    #define  B38400    0000017
    #define CSIZE    0000060
    #define   CS5    0000000
    #define   CS6    0000020
    #define   CS7    0000040
    #define   CS8    0000060
    #define CSTOPB    0000100
    #define CREAD    0000200
    #define CPARENB    0000400
    #define CPARODD    0001000
    #define HUPCL    0002000
    #define CLOCAL    0004000
    #define CIBAUD    03600000        /* input baud rate (not used) */
    #define CRTSCTS    020000000000        /* flow control */
    /* c_lflag bits */
    #define ISIG    0000001
    #define ICANON    0000002
    #define XCASE    0000004
    #define ECHO    0000010
    #define ECHOE    0000020
    #define ECHOK    0000040
    #define ECHONL    0000100
    #define NOFLSH    0000200
    #define TOSTOP    0000400
    #define ECHOCTL    0001000
    #define ECHOPRT    0002000
    #define ECHOKE    0004000
    #define FLUSHO    0010000
    #define PENDIN    0040000
    #define IEXTEN    0100000
    /* tcflow() and TCXONC use these */
    #define    TCOOFF        0
    #define    TCOON        1
    #define    TCIOFF        2
    #define    TCION        3
    /* tcflush() and TCFLSH use these */
    #define    TCIFLUSH    0
    #define    TCOFLUSH    1
    #define    TCIOFLUSH    2
    /* tcsetattr uses these */
    #define    TCSANOW        0
    #define    TCSADRAIN    1
    #define    TCSAFLUSH    2
    typedef int speed_t;
    extern speed_t cfgetispeed(struct termios *termios_p);
    extern speed_t cfgetospeed(struct termios *termios_p);
    extern int cfsetispeed(struct termios *termios_p, speed_t speed);
    extern int cfsetospeed(struct termios *termios_p, speed_t speed);
    extern int tcdrain(int fildes);
    extern int tcflow(int fildes, int action);
    extern int tcflush(int fildes, int queue_selector);
    extern int tcgetattr(int fildes, struct termios *termios_p);
    extern int tcsendbreak(int fildes, int duration);
    extern int tcsetattr(int fildes, int optional_actions,struct termios *termios_p);

    /// @typedef enum ser_parity_t
    /// @brief serial parity values
    typedef enum{
    PAR_N='N',
    PAR_O='O',
    PAR_E='E'
    }ser_parity_t;

    /// @typedef enum ser_flow_t
    /// @brief serial flow control values
    typedef enum{
    FLOW_N='N',
    FLOW_H='H',
    FLOW_X='X'
    }ser_flow_t;

    /// @typedef enum ser_csize_t
    /// @brief serial character size values
    typedef enum{
    CSZ_5=5,
    CSZ_6=6,
    CSZ_7=7,
    CSZ_8=8
    }ser_csize_t;

    /// @typedef enum ser_cstop_t
    /// @brief serial stop bit values
    typedef enum{
    CSB_0=0,
    CSB_1=1,
    CSB_2=2,
    }ser_cstop_t;

    /// @typedef enum ser_speed_t
    /// @brief serial IO speed values
    typedef enum{
    BPS_INVALID=-1,
    BPS_4800=4800,
    BPS_9600=9600,
    BPS_19200=19200,
    BPS_38400=38400
    }ser_speed_t;

    typedef struct termios TERM_T;

    typedef struct cwg_ser_s{
    /// @var cwg_ser_s::device
    /// @brief serial device path
    char *device;
    /// @var cwg_ser_s::speed
    /// @brief serial IO speed
    int speed;
    /// @var cwg_ser_s::csize
    /// @brief character size
    int csize;
    /// @var cwg_ser_s::cstop
    /// @brief character stop bits
    int cstop;
    /// @var cwg_ser_s::par
    /// @brief parity (enum)
    ser_parity_t par;
    /// @var cwg_ser_s::flow
    /// @brief flow control (enum)
    ser_flow_t flow;
    /// @var cwg_ser_s::fd
    /// @brief serial device file descriptor
    int fd;
    /// @var cwg_ser_s::term
    /// @brief terminal control structure
    struct termios term;
    /// @var cwg_ser_s::oterm
    /// @brief terminal saved settings
    TERM_T oterm;
    }cwg_ser_t;

    typedef unsigned long time_t;

    cwg_ser_t *cwg_ser_new(const char *device);
    int cwg_ser_show(cwg_ser_t *self, bool verbose, int indent);
    int cwg_ser_connect(cwg_ser_t *self, int speed, int csize, ser_parity_t parity, int cstop, ser_flow_t flow);
    int cwg_ser_disconnect(cwg_ser_t *self);
    int32_t cwg_write_delay(cwg_ser_t *self, char *src, size_t len, int32_t delay_msec);
    void cwg_ser_destroy(cwg_ser_t **pself);
    uint32_t cwg_upload_file(cwg_ser_t *self, char *dest, char *src, bool binary);
    uint32_t cwg_download_file(cwg_ser_t *self, char *dest, char *src, bool binary);
    int32_t cwg_read_until(cwg_ser_t *self, char *term, char *dest, size_t len, int tmout_sec);
    int cwg_ser_set_device(cwg_ser_t *self, const char *device);
    int32_t cwg_wdrain_rx(cwg_ser_t *self, time_t tmout_sec);
    """)

cwg = ffi.dlopen("../bin/libcwg.so")

devRefresh()
cbox_ser_device.set("")

####################
# CWG serial device instance
ser = cwg.cwg_ser_new(device_dfl.encode("utf-8"))

# Let the root wait for any events
root.mainloop()


