# -*- coding: utf-8 -*-
"""
Created on Jan 2 2022

@author: Patrik Petrýdes, Pavel Kovář
"""

import tkinter
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import matplotlib
matplotlib.use("TkAgg")
# import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
import numpy as np
from scipy.fftpack import fftshift
import os
from matplotlib.widgets import Cursor
from tkinter.filedialog import asksaveasfilename
import socket
import threading
from threading import *
import time
from time import perf_counter
import sys
import traceback
from configparser import ConfigParser


def server_connect_button():
    if server_connect.get() == False:
        print("Connect")
        print(ip_address.get())
        print(socket_no.get())
        try:
            root.spec_client = socket.socket(
                socket.AF_INET, socket.SOCK_STREAM)
        except socket.error:
            #print('Failed to create socket')
            connect_status.set("Socket Error")
            return
        ROBOT_IP = ip_address.get()
        ROBOT_PORT = int(socket_no.get())
        # Connect the socket object to the robot using IP address (string) and port (int)
        root.spec_client.connect((ROBOT_IP, ROBOT_PORT))
        server_connect.set(True)
        connect_status.set("Connected")
        root.after(1, thread_connect)

def save_config():
    print("Saving configuration")
    config = ConfigParser()
    config['main_section'] = {
        'SERVER_IP_ADDR': ip_address.get(),
        'SOCKET': socket_no.get(),
        'Spectrum length': waterfall_fbins,
        'Spectrum depth': waterfall_depth,
        'Frequency offset': freq_offset,
        'Sampling period': sampling_period
    }

    with open('SPECTRUM_PLOT.INI', 'w') as output_file:
        config.write(output_file)


def stop_threads():
    print("Program Exit")
    if server_connect.get() == True:
        print("Disconnect")
        root.spec_client.close()
    sys.exit()


def refresh():
    global tcp_state
    global spec
    global waterfall
    global rline
    global x1, x2
    global sampling_period
    global yticks
    global time_delay
    time_start = perf_counter()
    while True:
        msg = root.spec_client.recv(1)
        if msg[0] == 127:
            spec = root.spec_client.recv(waterfall_fbins)
            if len(spec) == waterfall_fbins:
                break
            spec = spec + root.spec_client.recv(waterfall_fbins - len(spec))
            if len(spec) == waterfall_fbins:
                break
# Intire row recived
    s = np.array(list(spec))
# Spectrum calculation and fftshift
    e = s//8
    v = s % 8
    ss = fftshift(0.2 * np.log10(v * (2 ** e) + 0.1))
    waterfall[1:waterfall_depth - 0, :] = waterfall[0:waterfall_depth - 1, :]
    waterfall[0, :] = ss[:]
    rline += 1
    if rline == rline_max:
        if dimension_var.get() == '2D':
            draw_2D()
        elif dimension_var.get() == '3D':
            draw_3D()
        rline = 0
    time_stop = perf_counter()
    time_delay = time_stop - time_start


def draw_2D():
    specplot.set_data(waterfall[t1:t2, x1:x2])
    sbandplt.draw_artist(specplot)
    if var_cursor.get() == 1:
        sbandplt.draw_artist(annot)
    fspec.canvas.blit(sbandplt.bbox)
    # print(time.time())


def draw_3D():
    sbandplt_3D.cla()
    sbandplt_3D.plot_surface(X, Y, waterfall[t1:t2 + 1, x1:x2 + 1], lw=0, cmap=cmap_set,
                             rcount=r_count, ccount=c_count)
    sbandplt_3D.set(xlim=[x1, x2 + 1])
    sbandplt_3D.set_xticks(xticks)
    sbandplt_3D.set_xticklabels(xtick_labels)
    sbandplt_3D.set_xlabel("Frequency offset [kHz]")
    sbandplt_3D.set_yticks(yticks)
    sbandplt_3D.set_yticklabels(yticklabels)
    sbandplt_3D.set_ylabel("Time [s]")
    sbandplt_3D.set_zticks(z_ticks)
    sbandplt_3D.set_zlabel(z_label)
    fspec.canvas.draw()

def test():
    global waterfall
    global rline
    global x1, x2
    global sampling_period
    global yticks
    global time_delay
    time_test_start = perf_counter()
    waterfall[1:waterfall_depth - 0, :] = waterfall[0:waterfall_depth - 1, :]
    wat_a = sorted(np.random.random(int(waterfall_fbins / 2)))
    wat_b = sorted(np.random.random(int(waterfall_fbins / 2)), reverse=True)
    waterfall[0, :] = np.concatenate((wat_a, wat_b))
    rline = rline + 1
    if rline == rline_max:
        if dimension_var.get() == '2D':
            draw_2D()
        elif dimension_var.get() == '3D':
            draw_3D()
        rline = 0
    time_test_stop = perf_counter()
    time_delay = time_test_stop - time_test_start

time_delay = 0

# Timer for receiving data
def every(delay, task):
    global time_delay, rline_max
  # time_test = time_test + time_test
    next_time = time.time() + delay
    while True:
        time.sleep(max(time_delay, next_time - time.time()))
        # time.sleep(max(time_delay, next_time - time.time() + sampling_period*rline_max))
        try:
            task()
        except Exception:
            traceback.print_exc()
    # skip tasks if we are behind schedule:
    #     next_time += (time.time() - next_time) // delay * delay + delay
        next_time += delay


def thread_test():
    global sampling_period
    threading.Thread(target=lambda: every(sampling_period, test)).start()

def thread_connect():
    global sampling_period
    threading.Thread(target=lambda: every(sampling_period, refresh)).start()


ip_adr = "localhost"
ip_socket = "8888"

spec = 0
waterfall_fbins = 1024
waterfall_depth = 600
rline = 0
x1 = 0
x2 = waterfall_fbins-1
t1 = 0
t2 = waterfall_depth-1
rline_max = 10
sampling_period = 1
freq_offset = 9600
z_min = 0
z_max = 1
z_label = 'Amplitude [dB]'
r_count = 25
c_count = 25


# Nacteni konfigurace
config = ConfigParser()
config.read('SPECTRUM_PLOT.INI')
print('Sections: %s' % config.sections())
if 'main_section' in config.sections():
    print(config['main_section'].get('SERVER_IP_ADDR'))
    ip_adr = config['main_section'].get('SERVER_IP_ADDR')
    print(config['main_section'].get('SOCKET'))
    ip_socket = config['main_section'].get('SOCKET')
    waterfall_depth = config['main_section'].get('Spectrum depth')
    waterfall_fbins = config['main_section'].get('Spectrum length')
    freq_offset = config['main_section'].get('Frequency offset')
    sampling_period = config['main_section'].getfloat('Sampling Period')
else:
    print('Main section does not exist in config.')
waterfall_fbins = int(waterfall_fbins)
waterfall_depth = int(waterfall_depth)
freq_offset = int(freq_offset)
spect_len_half = int(waterfall_fbins/2)

waterfall = np.empty([waterfall_depth, waterfall_fbins])
waterfall[:, :] = np.nan

# Setting up GUI
root = Tk()

root.title("Waterfall Spectrum Viewer")
root.geometry("1000x700")
# root.resizable(0, 0)  # Don't allow resizing in the x or y direction
root.resizable(True, True)  # Allow resizing in the x or y direction
root.protocol("WM_DELETE_WINDOW", stop_threads)
root.columnconfigure(0, weight=1)
root.rowconfigure(1, weight=1)
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky='nsew')

connect_status = StringVar()
connect_status.set("Disconnect")

ip_address = StringVar()
ip_address.set(ip_adr)
socket_no = StringVar()
socket_no.set(ip_socket)

server_connect = BooleanVar()
server_connect.set(False)

sbrow = 1

# Setting up Figure
fspec = Figure(figsize=(6, 4), dpi=100)

cmap_set = 'jet'
sbandplt = fspec.add_subplot(111)
specplot = sbandplt.imshow(waterfall, cmap=cmap_set, aspect='auto', vmin=z_min, vmax=z_max)
                           # , animated=True)
cbar = fspec.colorbar(specplot, orientation='vertical', ax=sbandplt)
z_ticks = np.linspace(z_min, z_max, num=6)
cbar.ax.set_yticks(z_ticks)
cbar.ax.set_ylabel(z_label)
xticks = np.array(np.linspace(0, waterfall_fbins, num=5))
sbandplt.set_xticks(xticks)
xtick_labels = np.array(np.linspace(-freq_offset, freq_offset, num=5))
sbandplt.set_xticklabels(xtick_labels)
sbandplt.set_xlabel("Frequency offset [kHz]")
yticks = np.array(np.linspace(0, waterfall_depth, num=7))
yticks = np.round(yticks)
yticklabels = yticks * sampling_period
sbandplt.set_yticks(yticks)
sbandplt.set_yticklabels(yticklabels)
sbandplt.set_ylabel("Time [s]")

canvas = FigureCanvasTkAgg(fspec, master=root)  # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().grid(column=0, columnspan=2, row=1, sticky='nsew')

# Cropping
def crop():
    global x1, x2
    global t1, t2
    global f1, f2
    global X, Y
    global xticks, xtick_labels, yticklabels
    f1 = float(x1_axis.get())
    f2 = float(x2_axis.get())
    t1_test = int(t1_axis.get())
    t2_test = int(t2_axis.get())
    if f1 >= -freq_offset and f2 <= freq_offset and f1 < f2\
            and t1_test>=0 and t2_test<=waterfall_depth and t1_test<t2_test:
        ticks_array = np.linspace(-freq_offset, freq_offset, num=waterfall_fbins)
        t1 = t1_test
        t2 = t2_test
        yticklabels = np.linspace(t1, t2, num=7)
        yticklabels = np.round(yticklabels)
        xtick_labels = np.linspace(f1, f2, num=5)
        difference_array_1 = np.absolute(ticks_array - f1)
        x1 = difference_array_1.argmin()
        difference_array_2 = np.absolute(ticks_array - f2)
        x2 = difference_array_2.argmin()
        if dimension_var.get() == '2D':
            sbandplt.set_yticklabels(yticklabels)
            sbandplt.set_xticklabels(xtick_labels)
        if dimension_var.get() == '3D':
            x_new = x[x1:x2+1]
            y_new = y[t1:t2+1]
            X, Y = np.meshgrid(x_new, y_new)
            sbandplt_3D.set_xticklabels(xtick_labels)
            sbandplt_3D.set_yticklabels(yticklabels)
        fspec.canvas.draw_idle()
    else:
        messagebox.showerror("Invalid values", "Cropping can only crop existing part of a plot.\n"
                        "Please choose values f₁,f₂,t₁ and t₂ "
                        "in chosen maximum values of frequency offset and time."
                        "Current limits are: \n"
                        "["+str(-freq_offset)+", "+str(freq_offset)+"] for frequency offset and "
                        "[0, "+str(waterfall_depth)+"]for time.\n"
                        "f₁ must be lower than f₂ and t₁ must be lower than t₂.")


def reset():
    global X, Y
    global x1, x2
    global xticks, xtick_labels
    xticks = np.array(np.linspace(0, waterfall_fbins, num=5))
    xtick_labels = np.array(np.linspace(-freq_offset, freq_offset, num=5))
    yticklabels = yticks * sampling_period
    if dimension_var.get() == '2D':
        reset_set(sbandplt, xticks, xtick_labels, yticklabels)
    if dimension_var.get() == '3D':
        X, Y = np.meshgrid(x, y)
        reset_set(sbandplt_3D, xticks, xtick_labels, yticklabels)
        sbandplt_3D.set_ylabel("Time [s]")
    x1 = 0
    x2 = waterfall_fbins-1
    resize_x1.delete(0, END)
    resize_x1.insert(END, -freq_offset)
    resize_x2.delete(0, END)
    resize_x2.insert(END, freq_offset)
    resize_t1.delete(0, END)
    resize_t1.insert(END, '0')
    resize_t2.delete(0, END)
    resize_t2.insert(END, waterfall_depth)
    # specplot.set_data(waterfall)
    fspec.canvas.draw_idle()
    dimension_var.set(dimensions[0])


def reset_set(plot, xticks, xtick_labels, yticklabels):
    plot.set_xticks(xticks)
    plot.set_xticklabels(xtick_labels)
    plot.set_yticklabels(yticklabels)


# Interpolaton
def interpolate(*args):
    specplot.set_interpolation(variable_int.get())
    specplot.set_interpolation_stage(variable_intstage.get())
    fspec.canvas.draw_idle()


# Cursor
def cursor_enable():
    global cursor
    global annot
    global coord
    global x_axis
    if var_cursor.get() == 1:
        cursor = Cursor(sbandplt, useblit=False, color='red', linewidth=0)
        annot = sbandplt.annotate("", xy=(0,0), xytext=(-20,20),textcoords="offset points",
                    bbox=dict(boxstyle='round4', fc='linen', ec='k', lw=1),
                    arrowprops=dict(arrowstyle='-|>'))
        annot.set_visible(False)
        coord = []

    else:
        cursor = 0
        annot.set_visible(False)
        fspec.canvas.draw_idle()


def onclick(event):
    global coord
    if var_cursor.get() == 1 and event.xdata is not None and event.ydata is not None:
        coord.append((event.xdata, event.ydata))
        x = event.xdata
        y = event.ydata
        annot.xy = (x, y)

        ticks_array = np.linspace(f1, f2, num=waterfall_fbins)
        x_ticks_array = np.linspace(0, waterfall_fbins-1, num=waterfall_fbins)
        difference_array_cursor = np.absolute(x_ticks_array - x)
        x = difference_array_cursor.argmin()
        x_edited = ticks_array[x]
        y_edited = y * sampling_period/(waterfall_depth/t2)
        m = waterfall[int(y_edited), int(x)]
        text = "f={:.3f}kHz, t={:.1f}s,\n A={:.3f}dB".format(x_edited, y_edited, m)
        annot.set_text(text)
        annot.set_visible(True)
        fspec.canvas.draw_idle()
fspec.canvas.mpl_connect('button_press_event', onclick)

# Colormap
def cmap(*args):
    global cmap_set
    cmap_set = variable_cmap.get()
    specplot.set_cmap(variable_cmap.get())
    fspec.canvas.draw_idle()


# Options menu
def open_options_menu():
    global spect_len_var, spect_dep_var, spect_f_var, sampling_period_e,\
        z_min_var, z_max_var, z_unit_var, c_count_var, r_count_var
    newWindow = Toplevel(root)
    newWindow.title("Options")
    newWindow.geometry("400x400")

# Starting parameters
    spectrum_frame = LabelFrame(newWindow, text="Setting up starting parameters", width=380, height=160)
    spectrum_frame.grid(column=0, row=0, sticky='nsew')
    spectrum_frame.grid_propagate(False)
    spectrum_frame.place(x=200, y=80, anchor="center")

    spect_len_var = StringVar()
    spect_len = ttk.Entry(spectrum_frame, textvariable=spect_len_var, width=10)
    spect_len.grid(column=0, row=1, sticky='n')
    spect_len.insert(END, waterfall_fbins)

    spect_dep_var = StringVar()
    spect_dep = ttk.Entry(spectrum_frame, textvariable=spect_dep_var, width=10)
    spect_dep.grid(column=3, row=1, sticky='n')
    spect_dep.insert(END, waterfall_depth)

    spect_f_var = StringVar()
    spect_f = ttk.Entry(spectrum_frame, textvariable=spect_f_var, width=10)
    spect_f.grid(column=6, row=1, sticky='n')
    spect_f.insert(END, freq_offset)

    z_min_var = StringVar()
    z_min_e = ttk.Entry(spectrum_frame, textvariable=z_min_var, width=10)
    z_min_e.grid(column=3, row=3, sticky='n')
    z_min_e.insert(END, z_min)

    z_max_var = StringVar()
    z_max_e = ttk.Entry(spectrum_frame, textvariable=z_max_var, width=10)
    z_max_e.grid(column=3, row=4, sticky='n')
    z_max_e.insert(END, z_max)

    sampling_period_e = StringVar()
    sampling = ttk.Entry(spectrum_frame, textvariable=sampling_period_e, width=10)
    sampling.grid(column=0, row=3, sticky='n')
    sampling.insert(END, sampling_period)

    ttk.Label(spectrum_frame, text="Spectrum Length", width=16).grid(column=0, row=0, columnspan=2, sticky='w')
    ttk.Label(spectrum_frame, text="Spectrum Depth", width=15).grid(column=3, columnspan=2, row=0, sticky='w')
    ttk.Label(spectrum_frame, text="Frequency Offset", width=15).grid(column=6, columnspan=2, row=0, sticky='w')
    ttk.Label(spectrum_frame, text="kHz", width=4).grid(column=7, row=1, sticky='w')
    ttk.Label(spectrum_frame, text="Sampling rate", width=15).grid(column=0, columnspan=2, row=2, sticky='w')
    ttk.Label(spectrum_frame, text="s", width=2).grid(column=1, row=3, sticky='w')
    ttk.Label(spectrum_frame, text="Min/max\namplitude", width=10).grid(column=3, row=2, sticky='n')
    ttk.Label(spectrum_frame, text="dB", width=3).grid(column=4, row=3, rowspan=2, sticky='nsw')

    ttk.Label(spectrum_frame, text="", width=3).grid(column=2, row=0)
    ttk.Label(spectrum_frame, text="", width=3).grid(column=5, row=0)

    ttk.Button(spectrum_frame, text="Set", command=start, width=10)\
        .grid(column=0, row=4, sticky='nsew', pady=5)

    ttk.Button(spectrum_frame, text="\u003F", command=info_spectrum,
                                width=3).place(x=378, y=-10, anchor=NE)

# 3D resolution
    threeD_frame = LabelFrame(newWindow, text="Setting up 3D plot resolution", width=380, height=100)
    threeD_frame.grid(column=0, row=0, sticky='nsew')
    threeD_frame.grid_propagate(False)
    threeD_frame.place(x=200, y=210, anchor="center")

    ttk.Label(threeD_frame, text="Row count", width=10).grid(column=0, row=0, sticky='w')
    ttk.Label(threeD_frame, text="Column count", width=15).grid(column=1, row=0, sticky='w')

    r_count_var = StringVar()
    r_count_e = ttk.Entry(threeD_frame, textvariable=r_count_var, width=10)
    r_count_e.grid(column=0, row=1, sticky='n')
    r_count_e.insert(END, r_count)

    c_count_var = StringVar()
    c_count_e = ttk.Entry(threeD_frame, textvariable=c_count_var, width=10)
    c_count_e.grid(column=1, row=1, sticky='n')
    c_count_e.insert(END, c_count)

    ttk.Button(threeD_frame, text="Set", command=threeD_res, width=10)\
        .grid(column=0, row=2, sticky='nsew', pady=5)

    ttk.Button(threeD_frame, text="\u003F", command=info_threeD_res,
                                width=3).place(x=378, y=-10, anchor=NE)


# Saving plot
def save_as():
    files = [('JPEG', '*.jpg'),
             ('PNG', '*.png'),
             ('Tiff', '*.tiff'),
             ('Svg', '*.svg'),
             ('Rgba', '*.rgba'),
             ('CSV', '*.csv')]
    filepath = asksaveasfilename(filetypes=files,
                                 defaultextension='.jpg')

    if filepath is None:    # returns `None` if dialog is closed with "cancel".
        return
    elif filepath.endswith('.csv'):
        np.savetxt(filepath, waterfall, delimiter=",")
    else:
        fspec.savefig(filepath)

# Test version
def test_version():
    messagebox.showinfo("Testing versing", "This opens a testing version of the program.\n"
                                "Test button will be generated. \n"
                                "Clicking Test button will start generating random input data.")

    ttk.Button(mainframe, text="Test", command=thread_test,
               width=32).grid(column=1, row=sbrow, sticky=W)


# 2D/3D - dimensions of the plot
def twoD():
    global sbandplt
    global specplot
    global waterfall
    fspec.clf()
    sbandplt = fspec.add_subplot(111)
    specplot = sbandplt.imshow(waterfall, cmap=cmap_set, aspect='auto', vmin=z_min, vmax=z_max)
    cbar = fspec.colorbar(specplot, orientation='vertical',  ax=sbandplt)
    sbandplt.set_xticks(xticks)
    sbandplt.set_xticklabels(xtick_labels)
    sbandplt.set_xlabel("Frequency offset [kHz]")
    sbandplt.set_yticks(yticks)
    sbandplt.set_yticklabels(yticklabels)
    sbandplt.set_ylabel("Time [s]")
    cbar.ax.set_yticks(z_ticks)
    cbar.ax.set_ylabel(z_label)
    fspec.canvas.draw_idle()


def threeD():
    global sbandplt_3D
    global specplot
    fspec.clf()
    sbandplt_3D = fspec.add_subplot(111, projection='3d')
    specplot.set_cmap(variable_cmap.get())
    sbandplt_3D.set_xticks(xticks)
    sbandplt_3D.set_xticklabels(xtick_labels)
    sbandplt_3D.set_yticks(yticks)
    sbandplt_3D.set_yticklabels(yticklabels)
    sbandplt_3D.set_ylabel("Time [s]")
    sbandplt_3D.set_xlabel("Frequency offset [kHz]")
    sbandplt_3D.set_zticks(z_ticks)
    sbandplt_3D.set_zlabel(z_label)
    fspec.canvas.draw_idle()


def threeD_res():
    global r_count, c_count
    r_count = int(r_count_var.get())
    c_count = int(c_count_var.get())


# Starting parameters
def start():
    global waterfall_fbins, waterfall_depth, freq_offset, waterfall, sampling_period
    global xticks, xtick_labels, yticks, yticklabels, x2, t2, z_label, z_min, z_max, z_ticks
    global specplot, sbandplt
    fspec.clf()
    sbandplt = fspec.add_subplot(111)
    waterfall_fbins = int(spect_len_var.get())
    waterfall_depth = int(spect_dep_var.get())
    freq_offset = int(spect_f_var.get())
    sampling_period = float(sampling_period_e.get())
    z_min = float(z_min_var.get())
    z_max = float(z_max_var.get())
    waterfall = np.empty([waterfall_depth, waterfall_fbins])
    waterfall[:, :] = np.nan
    specplot = sbandplt.imshow(waterfall, cmap=cmap_set, vmin=z_min, vmax=z_max, aspect='auto')
    cbar = fspec.colorbar(specplot, orientation='vertical', ax=sbandplt)
    z_ticks = np.linspace(z_min, z_max, num=6)
    xticks = np.array(np.linspace(0, waterfall_fbins, num=5))
    sbandplt.set_xticks(xticks)
    xtick_labels = np.array(np.linspace(-freq_offset, freq_offset, num=5))
    sbandplt.set_xticklabels(xtick_labels)
    sbandplt.set_xlabel("Frequency offset [kHz]")
    yticks = np.array(np.linspace(0, waterfall_depth, num=7))
    yticks = np.round(yticks)
    yticklabels = yticks * sampling_period
    sbandplt.set_yticks(yticks)
    sbandplt.set_yticklabels(yticklabels)
    sbandplt.set_ylabel("Time [s]")
    cbar.ax.set_yticks(z_ticks)
    z_label = 'Amplitude [dB]'
    cbar.ax.set_ylabel(z_label)

    dimension_var.set(dimensions[0])
    resize_x1.delete(0, END)
    resize_x1.insert(END, -freq_offset)
    resize_x2.delete(0, END)
    resize_x2.insert(END, freq_offset)
    resize_t1.delete(0, END)
    resize_t1.insert(END, 0)
    resize_t2.delete(0, END)
    resize_t2.insert(END, waterfall_depth)
    # x1 = 0
    x2 = waterfall_fbins
    t2 = waterfall_depth
    fspec.canvas.draw()

# def stop():
#     # threading.Thread(target=lambda: every(sampling_period, test)).start()
#     threading.Thread(target=lambda: every(sampling_period, test)).stop()
#     # every().close()
#     # global running
#     # running = False

# Changing Row number
def row_line():
    global rline_max, rline
    if int(rline_e.get()) < rline:
        rline = int(rline_e.get())-1
    rline_max = int(rline_e.get())


# Info messages
def info_crop():
    messagebox.showinfo("Cropping plot", "Crop plot in selected limits of [f₁, f₂], where"
                                         "f₁ and f₂ are values of offset frequency.\n"
                                         "After choosing values select Crop to crop the plot. \n"
                                         "To see the whole original plot, select Reset.")


def info_interpolation():
    messagebox.showinfo("Interpolating plot", "Interpolate data with selected interpolation method"
                                " in interpolation stage.\n"
                                "Selecting method or interpolation stage in dropdown menu will "
                                "immediately interpolate data.\n"
                                "Default interpolation is 'Antialiased' and default interpolation stage is 'data'.\n"
                                "To disable interpolation select 'None' in dropdown menu.\n"
                                "\n"
                                "Interpolation stage is a stage at which interpolation happen."
                                "If 'data' is selected, interpolation is carried out on the data provided and "
                                "if 'rgba' is selected, the interpolation is carried "
                                "out after the colormapping has been applied (visual interpolation).")


def info_cursor():
    messagebox.showinfo("Cursor", "Checking checkbox will enable cursor for 2D plot.\n"
                                  "Clicking anywhere in the plot afterward will show its values at the time.")


def info_cmap():
    messagebox.showinfo("Colormap", "Colormap is a color palette that sample the plot between two end "
                                    "colors based on its amplitude. \n"
                                    "Selecting colormap in dropdown menu will immediately change it.\n"
                                    "Works with both 2D and 3D versions of the plot.")


def info_dimension():
    messagebox.showinfo("Changing dimensions of the plot", "Selecting 2D or 3D button will change the displayed"
                                                        "plot's dimensions. \n"
                                                        "If 2D is selected only frequency offset"
                                                        " and time axes will be shown and if 3D "
                                                        "is selected amplitude axis will be shown as well.")


def info_rline():
    messagebox.showinfo("Setting Row number", "Row number represents how often is the plot drawn.\n"
                                "e.g. if the Row number is 10, then the plot will update itself "
                                "every 10 rows.")


def info_spectrum():
    messagebox.showinfo("Starting Parameters", "Clicking Set will change starting parameters.\n"
                                "Parameters are:\n"
                                "\n"
                                "Spectrum length is the length of the received signal. "
                                "Length should always be in powers of 2. e.g. 1024, 2048 and so on.\n"
                                "\n"
                                "Spectrum Depth sets the time limits of the displayed signal. Default depth is 600 s"
                                ", which shows only the spectrum in the last 600 s.\n"
                                "\n"
                                "Frequency Offset changes the limits of displayed spectrum in frequency domain."
                                "e.g. setting frequency offset to 9600 kHz will only show spectrum in values"
                                "of -9600 kHz to 9600 kHz.\n"
                                "\n"
                                "Sampling Period will change how often is data received. e.g. if Sampling Period is "
                                "1, new row will be drawn every second, if it is 2, row will be drawn every 2 seconds"
                                "and so on.\n"
                                "\n"
                                "Minimum and maximum amplitude changes the max/min values in colormap and"
                                " on amplitude axis. Values are in dB.\n"
                                "\n"
                                "These parameters should only be changed before starting receiving data.")

def info_threeD_res():
    messagebox.showinfo("Setting 3D plot resolution", "Row count and Column count represent"
                                " the maximum number of samples used in that direction.\n"
                                "Decreasing Row count and Column count will decrease the plot resolution "
                                "in that axis but increase drawing speed.")

# Mainframe buttons
ttk.Label(mainframe, text="Server IP Address", width=32).grid(
    column=1, row=sbrow, sticky=W)
ttk.Entry(mainframe, textvariable=ip_address,
               width=32).grid(column=2, row=sbrow, sticky=W)

sbrow += 1
ttk.Label(mainframe, text="Socket").grid(column=1, row=sbrow, sticky=W)
ttk.Entry(mainframe, textvariable=socket_no,
               width=32).grid(column=2, row=sbrow, sticky=W)

sbrow += 1
connect_button = ttk.Button(mainframe, text="Connect", command=server_connect_button,
           width=32).grid(column=1, row=sbrow)
ttk.Label(mainframe, textvariable=connect_status).grid(
    column=2, row=sbrow, sticky=W)

sbrow += 1
ttk.Button(mainframe, text="Save Configuration", command=save_config,
           width=32).grid(column=1, row=sbrow, sticky=W)

# sbrow += 1
# ttk.Button(mainframe, text="Test", command=thread_test,
#            width=32).grid(column=1, row=sbrow, sticky=W)

sbrow += 1
ttk.Button(mainframe, text="Exit", command=stop_threads,
           width=32).grid(column=1, row=sbrow, sticky=W)
sbrow += 1


# Functions
functions = LabelFrame(root, text="Functions", width=300, height=600, padx=5, pady=5)
functions.grid(column=3, row=0, rowspan=3, sticky='ne')
functions.grid_propagate(False)

frow = 1
# Cropping plot
frow += 1
resize_frame = LabelFrame(functions, text="Crop plot", width=280, height=100)
resize_frame.grid(column=0, row=frow, sticky='nsew')
resize_frame.grid_propagate(False)

x1_axis = StringVar()
resize_x1 = ttk.Entry(resize_frame, textvariable=x1_axis, width=10)
resize_x1.grid(column=1, row=0)
resize_x1.insert(END, -freq_offset)
x2_axis = StringVar()
resize_x2 = ttk.Entry(resize_frame, textvariable=x2_axis, width=10)
resize_x2.grid(column=5, row=0)
resize_x2.insert(END, freq_offset)
spacer = ttk.Label(resize_frame, text="", width=3).grid(column=3, row=0)
t1_axis = StringVar()
resize_t1 = ttk.Entry(resize_frame, textvariable=t1_axis, width=10)
resize_t1.grid(column=1, row=1)
resize_t1.insert(END, '0')
t2_axis = StringVar()
resize_t2 = ttk.Entry(resize_frame, textvariable=t2_axis, width=10)
resize_t2.grid(column=5, row=1)
resize_t2.insert(END, waterfall_depth)

ttk.Button(resize_frame, text="Crop", command=crop, width=15).grid(column=0, columnspan=3, row=2, sticky='n', pady=5)
ttk.Button(resize_frame, text="Reset", command=reset, width=15).grid(column=4, columnspan=3, row=2, sticky='n', pady=5)
ttk.Label(resize_frame, text="f₁:", width=2).grid(column=0, row=0, sticky='w')
ttk.Label(resize_frame, text="kHz", width=4).grid(column=2, row=0, sticky='w')
ttk.Label(resize_frame, text="f₂:", width=2).grid(column=4, row=0, sticky='e')
ttk.Label(resize_frame, text="kHz", width=4).grid(column=6, row=0, sticky='w')
f1 = float(x1_axis.get())
f2 = float(x2_axis.get())
x_axis = np.linspace(f1, f2, num=waterfall_fbins)
ttk.Label(resize_frame, text="t₁:", width=2).grid(column=0, row=1, sticky='w')
ttk.Label(resize_frame, text="s", width=3).grid(column=2, row=1, sticky='w')
ttk.Label(resize_frame, text="t₂:", width=2).grid(column=4, row=1, sticky='e')
ttk.Label(resize_frame, text="s", width=3).grid(column=6, row=1, sticky='w')

# Interpolation/decimation
frow += 1
interpolation_frame = LabelFrame(functions, text="Interpolate plot", width=250, height=80)
interpolation_frame.grid(column=0, row=frow, sticky='nsew')
interpolation_frame.grid_propagate(False)
variable_int = StringVar()
variable_int.set("Antialiased")
int_choices = ['None', 'Antialiased', 'Nearest', 'Bilinear', 'Bicubic', 'Spline16',
               'Spline36', 'Hanning', 'Hamming', 'Hermite', 'Kaiser', 'Quadric', 'Catrom',
               'Gaussian', 'Bessel', 'Mitchell']
variable_intstage = StringVar()
variable_intstage.set("Data")
intstage_choices = ['Data', 'Rgba']
ttk.Label(interpolation_frame, text="Interpolation", width=15).grid(column=0, row=0, sticky='w')
ttk.Label(interpolation_frame, text="Interpolation stage", width=20).grid(column=2, row=0, sticky='w')
option_int = OptionMenu(interpolation_frame, variable_int, *int_choices)
option_int.grid(column=0, row=1, sticky='ns')
option_intstage = OptionMenu(interpolation_frame, variable_intstage, *intstage_choices)
option_intstage.grid(column=2, row=1, sticky='ns')
variable_int.trace("w", interpolate)
variable_intstage.trace("w", interpolate)
ttk.Label(interpolation_frame, text="", width=4).grid(column=1, row=1)

# Cursor
frow += 1
cursor_frame = LabelFrame(functions, text="Cursor", width=250, height=60)
cursor_frame.grid(column=0, row=frow, sticky='nsew')
cursor_frame.grid_propagate(False)
var_cursor = IntVar()
cursor_checkbutton = Checkbutton(cursor_frame, text = "Enable cursor", variable=var_cursor, onvalue=1,
    offvalue=0, height=2, width=10, command=cursor_enable)\
    # .pack()
cursor_checkbutton.grid(column=0, row=0, sticky='nsew')

# Colormap
frow += 1
cmap_frame = LabelFrame(functions, text="Colormap", width=250, height=60)
cmap_frame.grid(column=0, row=frow, sticky='nsew')
cmap_frame.grid_propagate(False)
variable_cmap= StringVar()
variable_cmap.set("jet")
option_cmap = OptionMenu(cmap_frame, variable_cmap, "jet", "turbo", "rainbow", "gist_rainbow", "brg", "bwr",
                         "nipy_spectral", "cubehelix", "CMRmap", "ocean", "gnuplot", "gnuplot2")
option_cmap.grid(column=0, row=0, sticky='nsew', padx=10, pady=3)
variable_cmap.trace("w", cmap)


# 2D/3D plot
frow += 1
dimension_frame = LabelFrame(functions, text="2D/3D plot", width=250, height=60)
dimension_frame.grid(column=0, row=frow, sticky='nsew')
dimension_frame.grid_propagate(False)
dimension_var = tkinter.StringVar()
dimensions = ('2D', '3D')
radio_dim1 = ttk.Radiobutton(dimension_frame, text=dimensions[0], value=dimensions[0],
                             variable=dimension_var, command=twoD)
radio_dim1.grid(column=1, row=0, ipadx=15, ipady=10, sticky='nsew')
radio_dim2 = ttk.Radiobutton(dimension_frame, text=dimensions[1], value=dimensions[1],
                             variable=dimension_var, command=threeD)
dimension_var.set(dimensions[0])
radio_dim2.grid(column=2, row=0, ipadx=15, ipady=10, sticky='nsew')
ttk.Label(dimension_frame, text="", width=5).grid(column=0, row=0)
x = np.linspace(0, 1023, num=waterfall_fbins)
y = np.linspace(0, 599, num=600)
X, Y = np.meshgrid(x, y)

# Row number
frow += 1
rline_frame = LabelFrame(functions, text="Setting Row number", width=250, height=95)
rline_frame.grid(column=0, row=frow, sticky='nsew')
rline_frame.grid_propagate(False)
rline_e = StringVar()
rline_entry = ttk.Entry(rline_frame, textvariable=rline_e, width=10)
rline_entry.grid(column=0, row=1, padx=10)
rline_entry.insert(END, rline_max)
ttk.Button(rline_frame, text="Set", command=row_line, width=15)\
    .grid(column=0,columnspan=1, row=2, sticky='nsew', pady=5, padx=5)
spacer2 = ttk.Label(rline_frame, text="", width=5).grid(column=1, row=1)
ttk.Label(rline_frame, text="Row number:", width=12).grid(column=0, row=0, sticky='w', padx=10)

# Menubar
menubar = Menu(root)
main_menu = Menu(menubar, tearoff = 0)
menubar.add_cascade(label ='Menu', menu = main_menu)
main_menu.add_command(label ='Options', command = open_options_menu)
main_menu.add_command(label ='Save as ...', command = save_as)
main_menu.add_command(label ='Testing version', command = test_version)
main_menu.add_separator()
main_menu.add_command(label ='Exit', command = stop_threads)
root.config(menu = menubar)

# Help buttons
qbutton_crop = ttk.Button(resize_frame, text="\u003F", command=info_crop,
           width=3).place(x=278, y=-10,anchor=NE)
qbutton_int = ttk.Button(interpolation_frame, text="\u003F", command=info_interpolation,
           width=3).place(x=278, y=-10,anchor=NE)
qbutton_cursor = ttk.Button(cursor_frame, text="\u003F", command=info_cursor,
           width=3).place(x=278, y=-10,anchor=NE)
qbutton_cmap = ttk.Button(cmap_frame, text="\u003F", command=info_cmap,
           width=3).place(x=278, y=-10,anchor=NE)
qbutton_dimesion = ttk.Button(dimension_frame, text="\u003F", command=info_dimension,
           width=3).place(x=278, y=-10,anchor=NE)
qbutton_rline = ttk.Button(rline_frame, text="\u003F", command=info_rline,
           width=3).place(x=278, y=-10,anchor=NE)

# ttk.Button(mainframe, text="Stop", command=stop,
#            width=32).grid(column=2, row=sbrow-1, sticky=W)

root.mainloop()
