import numpy as np
import matplotlib.pyplot as plt
import scipy.io.wavfile as wav
import sys
import os
import datetime

from record import *
from generate_plot import generate_plot

# Qt utilities.
import PyQt5

from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import *

# QtCreator utilities.
import mainwindow_auto

#TKinter utilities.c
import tkinter as tk

from tkinter.filedialog import askopenfilename
from tkinter import *

sys.path.append("./..")

from DeepGate import DeepGate
from preprocess import resample
from preprocess import generate_fpga_data
from preprocess import generate_dataset
from train import train
#from network import DeepGate
#from network import resample
#from network import generate_dataset
#from network import train
#from network import test
from test import test
#from network import export_inputs
#from network import export_inputs
from export import export_inputs
from export import export_weights


class MainWindow(QMainWindow, mainwindow_auto.Ui_MainWindow):

    def __init__(self):
        super(self.__class__, self).__init__()
        self.setupUi(self)

        # Set up the path, dictionary, and sample sets.
        self.path = "./../"
        self.dictionary = list(range(10)) + ["east", "north", "south", "west"]
        self.people = ["michael_1", "michael_2", "michael_3", "michael_4", "michael_5",
                       "cedric_1", "cedric_2", "cedric_3", "cedric_4", "cedric_5",
                       "estella_1", "estella_2", "estella_3", "estella_4", "estella_5",
                       "iyan_1", "iyan_2", "iyan_3", "iyan_4", "iyan_5",
                       "sophia_1"]

        # Set up the network parameters.
        self.dnn = 0
        self.input_nodes = 516
        self.hidden_nodes = [100, 50]
        self.output_nodes = 14

        # Set up other system parameters.
        self.learning_rate = 0.05
        self.epochs = 10
        self.threshold = 0.4

        self.learning_rate_input.setText(str(self.learning_rate))
        self.epochs_input.setText(str(self.epochs))
        self.threshold_input.setText(str(self.threshold))

        # Store the training and testing data.
        self.training_data = 0
        self.testing_data = 0

        # Set up the button hooks.
        self.open_button.clicked.connect(lambda: self.pressed_open_button())
        self.record_button.clicked.connect(lambda: self.pressed_record_button())
        self.playback_button.clicked.connect(lambda: self.pressed_playback_button())
        self.initialize_button.clicked.connect(lambda: self.pressed_initialize_button())
        self.train_button.clicked.connect(lambda: self.pressed_train_button())
        self.test_button.clicked.connect(lambda: self.pressed_test_button())
        self.program_button.clicked.connect(lambda: self.pressed_program_button())
        self.generate_button.clicked.connect(lambda: self.pressed_generate_button())
        self.upload_button.clicked.connect(lambda: self.pressed_upload_button())
        self.classify_button.clicked.connect(lambda: self.pressed_classify_button())
        self.save_button.clicked.connect(lambda: self.pressed_save_button())

    def pressed_open_button(self):
        print("Pressed \"Open\"!")

        # Find a signal.
        print("Opened file:")
        self.statusDisplay.append("Opened file:")
        root = Tk()
        root.withdraw()
        filename = askopenfilename()

        # Return if no signal was chosen.
        if type(filename) != str:
            return

        # Copy the signal to the demo location.
        (rate, sig) = wav.read(filename) 
        wav.write(self.path + "network/speech_recordings/raw/demo/demo.wav", rate, sig)
        print(filename + "\n")
        self.statusDisplay.append(filename + "\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

        # Plot the untrimmed and trimmed versions of the signal.
        #generate_plot(filename)

    def pressed_record_button(self):

        print("Pressed \"Record\"!")

        # Set the path and the filename.
        path = self.path + "network/speech_recordings/"
        filename = "demo_44100.wav"

        # Record a signal.
        print("Please speak a number between 0 and 9, inclusive, or one of the cardinal directions into the microphone.\n")
        self.statusDisplay.append("Please speak a number between 0 and 9, inclusive, or one of the cardinal directions into the microphone.\n")   
        record_to_file(path + "raw/demo/" + filename)
        print("Results written to:")
        print(filename + "\n")
        self.statusDisplay.append("Results written to:")
        self.statusDisplay.append(path + "raw/demo/" + filename + "\n")
        
        # Resample the signal.
        print("Resampling Signal")
        resample(path, 16000)
        print("Resampling Complete")

        # Export the signal.
        print("Exporting signal...")
        self.statusDisplay.append("Exporting signal...")

        generate_fpga_data(path, self.path + "fpga/exe/input_files/",["demo"], ["demo"], "testing", self.threshold)

        print("Finished exporting!\n")
        self.statusDisplay.append("Finishing exporting!\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

        # Plot the untrimmed and trimmed versions of the signal.
        #generate_plot(path + filename)

    def pressed_playback_button(self):
        print("Pressed \"Playback!\"!")

        # Find a signal.
        print("Opened file:")
        self.statusDisplay.append("Opened file:")
        root = Tk()
        root.withdraw()
        filename = askopenfilename()

        # Playback the signal.
        if type(filename) == str:
            os.system("omxplayer " + filename)    
        print(filename + "\n")
        self.statusDisplay.append(filename + "\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

    def pressed_initialize_button(self):
        print("Pressed \"Initialize\"!")

        # Initialize the network.
        print("Initialize the network.")
        self.statusDisplay.append("Initialize the network.")
        self.dnn = DeepGate(self.input_nodes, self.hidden_nodes, self.output_nodes, self.learning_rate)     

        # Update system parameters.
        self.learning_rate = float(self.learning_rate_input.text())
        self.epochs = int(self.epochs_input.text())
        self.threshold = float(self.threshold_input.text())
        print("Learning rate: " + str(self.learning_rate))
        self.statusDisplay.append("Learning rate: " + str(self.learning_rate))
        print("Number of epochs: " + str(self.epochs))
        self.statusDisplay.append("Number of epochs: " + str(self.epochs))
        print("Threshold: " + str(self.threshold))
        self.statusDisplay.append("Threshold: " + str(self.threshold))

        # Set up the path.
        path = self.path + "network/speech_recordings/"

        # Generate the training and testing sets.
        temp = self.people[:-1]
        np.random.shuffle(temp)
        self.people = np.append(temp, self.people[-1])
        if self.development_radio_button.isChecked():
            generate_dataset(path, self.dictionary, self.people[:-1], "training", self.threshold)
            generate_dataset(path, self.dictionary, [self.people[-1]], "testing", self.threshold)
        else:
            generate_dataset(path, self.dictionary, self.people, "training", self.threshold)
            generate_dataset(path, ["demo"], ["demo"], "testing", self.threshold)

        # Read in the training and testing sets.
        with open(path + "training_set.csv", "r") as training_file:
            self.training_data = training_file.readlines()
        with open(path + "testing_set.csv", "r") as testing_file:
            self.testing_data = testing_file.readlines()
        print("Finished initialization!\n")
        self.statusDisplay.append("Finished initialization!\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

    def pressed_train_button(self):
        print("Pressed \"Train\"!")

        # Return if the network has been initialized.
        if self.dnn == 0:
            print("The network has not been initialized!\n")
            self.statusDisplay.append("The network has not been initialized!\n")     
            return 

        # Train the network.
        print("Training...")
        self.statusDisplay.append("Training...")        
        train(self.dnn, self.training_data, list(range(self.epochs)))
        print("Done training!\n")
        self.statusDisplay.append("Done training!\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

    def pressed_test_button(self):
        print("Pressed \"Test\"!")

        # Return if the network has been initialized.
        if self.dnn == 0:
            print("The network has not been initialized!\n")
            self.statusDisplay.append("The network has not been initialized!\n")     
            return 

        # Test the network.
        print("Testing...")
        self.statusDisplay.append("Testing...")        
        performance = []
        (outputs, scorecard) = test(self.dnn, self.testing_data)
        print("Done testing!\n")
        self.statusDisplay.append("Done testing!\n")

        # Display the results.
        result = np.argmax(outputs)
        performance = 100*np.sum(scorecard)/len(scorecard)
        self.performance_bar.setValue(performance)
        print("Performance: " + "{:.4}".format(str(performance)) + "%")
        self.statusDisplay.append("Performance: " + "{:.4}".format(str(performance)) + "%")
        print("The Raspberry Pi classified the word(s) as:")
        self.statusDisplay.append("The Raspberry Pi classified the word(s) as:")
        for output in outputs:
            print(str(np.argmax(output)))
            self.statusDisplay.append(str(np.argmax(output)))
            self.performance_box.setText(str(np.argmax(output)))
        print("")
        self.statusDisplay.append("")
        self.statusDisplay.moveCursor(QTextCursor.End)
            
    def pressed_program_button(self):
        print("Pressed \"Program\"!")

        # Program the FPGA.
        print("Programming the FPGA...")
        self.statusDisplay.append("Programming the FPGA...")
        os.chdir(self.path + "fpga/bit/")
        os.system("sudo djtgcfg enum")
        os.system("sudo djtgcfg init -d DeepGate")
        os.system("sudo djtgcfg prog -d DeepGate -i 0 -f 516_100_50_14.bit")
        os.chdir("../../gui/")
        print("Finished programming the FPGA!\n")
        self.statusDisplay.append("Finished programming the FPGA!\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

    def pressed_generate_button(self):
        print("Pressed \"Generate\"!")

        # Export the weights.
        if self.dnn != 0:
            export_weights(self.path + "fpga/exe/input_files/", self.dnn)

        # Generate the new weight file.
        print("Generating weights...")
        self.statusDisplay.append("Generating weights...")
        os.chdir(self.path + "fpga/exe/")
        os.system("sudo ./dg_gen_weights.exe")
        os.chdir("../../gui/")
        print("Finished generating weights!\n")
        self.statusDisplay.append("Finished generating weights!\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

    def pressed_upload_button(self):
        print("Pressed \"Upload\"!")

        # Upload the weights.
        print("Uploading weights...")
        self.statusDisplay.append("Uploading weights...")
        os.chdir(self.path + "fpga/exe/")
        os.system("sudo ./dg_upload_weights.exe")
        os.chdir("../../gui/")
        print("Finished uploading weights!\n")
        self.statusDisplay.append("Finished uploading weights!\n")
        self.statusDisplay.moveCursor(QTextCursor.End)

    def pressed_classify_button(self):
        print("Pressed \"Classify\"!")

        # Classify the signal.
        print("Classifying...")
        self.statusDisplay.append("Classifying...")
        os.chdir(self.path + "fpga/exe/")
        os.system("sudo ./dg_classify.exe")
        os.chdir("../../gui/")
        print("Finished classifying!\n")
        self.statusDisplay.append("Finished classifying!\n")

        # Print classification results.
        with open(self.path + "fpga/exe/output_files/capture.txt", "r") as file:
            outputs = file.readlines()
        result = np.argmax(np.asfarray(outputs[0].split(",")))
        if result > 9:
            if result == 10:
                result = "east"
            elif result == 11:
                result = "north"
            elif result == 12:
                result = "south"
            else:
                result = "west"
        print("The FPGA classified the word as:")
        self.statusDisplay.append("The FPGA classified the word as:")
        print(str(result))
        self.statusDisplay.append(str(result))
        self.performance_box.setText(str(result))
        self.statusDisplay.moveCursor(QTextCursor.End)

    def pressed_save_button(self):
        print("Pressed \"Save\"!")
        now = datetime.datetime.now()
        status_string = self.statusDisplay.toPlainText()
        log = str(now) + "\n\n" + status_string
        print("Creating a new text file...")
        log_file = open("log.txt", "w")
        log_file.write(log)
        log_file.close()
        print(log)
        self.statusDisplay.setText("Saved log!\n")

def main():
   app = QApplication(sys.argv)
   form = MainWindow()
   form.show()
   sys.exit(app.exec_())

if __name__ == "__main__":
     main()

