import matplotlib
matplotlib.use("TkAgg")

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style

import tkinter as tk
import tkinter.font as tkFont
from tkinter import ttk
from tkinter import filedialog

from collections import namedtuple

import webbrowser

import os

# Set Fonts
LARGE_FONT = ("Verdana", 12)
SMALL_FONT = ("Verdana", 10)

# Set Matplotlib Style
style.use('dark_background')

# Function to display a notification popup
def popupmsg(msg):
    popup = tk.Tk()

    def leavemini():
        popup.destroy()

    # Display the msg param as text
    popup.title("Notification")
    label = ttk.Label(popup, text = msg, font = LARGE_FONT)
    label.pack(side = "top", fill = "x", pady = 10, padx = 20)

    # Put in an Okay button to dismiss notification
    b1 = ttk.Button(popup, text = "Okay", command = leavemini)
    b1.pack(pady = 10, padx = 20)
    popup.mainloop()

# Display the animated graph
def display_graph(curve_list, cow_list):
    # Set colors for movement lines
    graph_colors = ['#FF0000','#008080','#0000FF','#00FF00','#008000','#FFFF00', '#800000',
                    '#808080', '#000000', '#808000', '#000080', '#FF0000', '#008080', '#0000FF',
                    '#00FF00', '#008000', '#FFFF00', '#800000', '#808080', '#000000', '#808000',
                    '#000080', '#FF0000', '#008080', '#0000FF', '#00FF00', '#008000', '#FFFF00',
                    '#800000', '#808080', '#000000', '#808000', '#000080']

    # Create the graph windows and set properties
    graph_page = tk.Toplevel()
    graph_page.title("Movement Graph")
    icon_img = tk.Image("photo", file="icon.png")
    graph_page.tk.call('wm','iconphoto',graph_page._w, icon_img)

    # Create matplotlib figure
    f = Figure()
    a = f.add_subplot(111)

    # Create empty list of curves
    current_curve_list = []
    Curve = namedtuple('Curve', 'x y')
    for curve in curve_list:
        current_curve_list.append(Curve([], []))

    # Draw the graph in the window
    canvas = FigureCanvasTkAgg(f, graph_page)
    canvas.draw()
    canvas.get_tk_widget().pack(side = tk.TOP, fill = tk.BOTH, expand = True)

    # Draw the matplotlib toolbar to the window
    toolbar = NavigationToolbar2Tk(canvas, graph_page)
    toolbar.update()
    canvas._tkcanvas.pack(side = tk.TOP, fill = tk.BOTH, expand = True)

    # Function to call every mainloop update to animate graph
    def animate(i):
        for j in range(len(curve_list)):
            if len(curve_list[j].x) > 0:
                x = curve_list[j].x.pop(0)
                y = curve_list[j].y.pop(0)

                current_curve_list[j].x.append(x)
                current_curve_list[j].y.append(y)

                a.plot(current_curve_list[j].x, current_curve_list[j].y, graph_colors[j])
        a.legend(cow_list)

    # Set the animation to run every 0.5 seconds
    ani = animation.FuncAnimation(f, animate, interval = 500)

    # Run the graph window main loop
    graph_page.geometry("800x600")
    graph_page.mainloop()

# Main controller class for the GUI
class Gui(tk.Tk):
    def __init__(self, data, plotter, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        # Save the reference to the data module
        self.data = data

        # Save reference to the plotter module
        self.plotter = plotter

        # Set title and icon
        self.title("Cattle Data Tool")
        icon_img = tk.Image("photo", file="icon.png")
        self.tk.call('wm','iconphoto',self._w, icon_img)

        # Create a main container for the app
        container = tk.Frame(self)
        container.pack(side = "top", fill = "both", expand = True)
        container.grid_rowconfigure(0, weight = 1)
        container.grid_columnconfigure(0, weight = 1)

        # Create instances of all frames
        self.frames = {}
        for f in (StartPage,):
            frame = f(container, self)
            self.frames[f] = frame
            frame.grid(row = 0, column = 0, sticky = "nsew")

        # Show the starting frame
        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

# Starting frame class
class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        self.parent = parent

        tk.Frame.__init__(self, self.parent)

        # Initialize the table
        self.header = ['ID', 'External ID', 'Internal ID', 'Day', 'Month', 'File Name']
        self.data = []
        self.table = Table(self.header, self.data)

        # Create the menu bar
        menubar = tk.Menu(self.controller)

        # Set file menu callbacks
        filemenu = tk.Menu(menubar, tearoff = 0)
        filemenu.add_command(
            label = "Save Workspace",
            command = lambda: self.save_workspace())
        filemenu.add_command(
            label = "Load Workspace",
            command = lambda: self.load_workspace())
        filemenu.add_separator()
        filemenu.add_command(
            label = "Exit",
            command = lambda: self.exit())
        menubar.add_cascade(label = "File", menu = filemenu)

        # Set data menu callbacks
        datamenu = tk.Menu(menubar, tearoff = 0)
        datamenu.add_command(
            label = "Import CSV",
            command = lambda: self.import_csv())
        datamenu.add_command(
            label = "Remove Selected",
            command = lambda: self.remove_selected())
        datamenu.add_command(
            label = "Plot Selected",
            command = lambda: self.plot_selected())
        menubar.add_cascade(label = "Data", menu = datamenu)

        # Set select menu callbacks
        selectmenu = tk.Menu(menubar, tearoff = 0)
        selectmenu.add_command(
            label = "Select All",
            command = lambda: self.select_all())
        selectmenu.add_command(
            label = "Select None",
            command = lambda: self.select_none())
        menubar.add_cascade(label = "Select", menu = selectmenu)

        # Set help menu callbacks
        helpmenu = tk.Menu(menubar, tearoff = 0)
        helpmenu.add_command(
            label = "Open Documentation",
            command = lambda: self.open_documentation())
        menubar.add_cascade(label = "Help", menu = helpmenu)

        tk.Tk.config(self.controller, menu = menubar)

        self.table.container.pack(fill = tk.BOTH, expand = True)

    # File menu functions
    def save_workspace(self):
        filename =  filedialog.asksaveasfilename(
            title = "Save Workspace", filetypes = (("cattle data file","*.cdf"),))
        if not filename.find(".cdf"):
            filename += ".cdf"
        self.controller.data.export_db(filename)

    def load_workspace(self):
        filename =  filedialog.askopenfilename(
            title = "Load Workspace", filetypes = (("cattle data file","*.cdf"),))
        if len(filename) > 1:
            self.data = self.controller.data.load_db(filename)
            self.table.update_tree(self.data)

    def exit(self):
        self.controller.destroy()

    # Data menu functions
    def import_csv(self):
        filenames =  filedialog.askopenfilenames(
            title = "Load Workspace", filetypes = (("comma-seperated values file","*.csv"),))
        for filename in filenames:
            self.data.append(self.controller.data.add_csv(filename))
        self.table.update_tree(self.data)

    def remove_selected(self):
        for (iid, id) in self.table.get_selected_ids():
            self.table.tree.delete(iid)
            self.controller.data.remove_by_id(id)

            for data in self.data:
                if data[0] == id:
                    self.data.remove(data)

    def plot_selected(self):
        Curve = namedtuple('Curve', 'x y')
        curve_list = []
        cow_list = []
        for (iid, id) in self.table.get_selected_ids():
            x, y = self.controller.plotter.plot(id)
            curve_list.append(Curve(x, y))
            cow_list.append("Cow " + str(id))
        display_graph(curve_list, cow_list)

    # Select Menu functions
    def select_all(self):
        iids = self.table.tree.get_children()
        self.table.tree.selection_set(iids)

    def select_none(self):
        iids = self.table.tree.get_children()
        self.table.tree.selection_remove(iids)

    # Help menu functions
    def open_documentation(self):
        webbrowser.open('docs.pdf')

# Table class to display cattle list, using a ttk Treeview
class Table:
    def __init__(self, header, data):
        self.header = header
        self.data = data
        self.tree = None
        self.setup_widgets()
        self.build_tree()

    # Update the tree with new data
    def update_tree(self, data):
        self.data = data
        for item in self.tree.get_children():
            self.tree.delete(item)

        for item in self.data:
            self.tree.insert('', 'end', values = item)

    # Get ID's of the selected cows
    def get_selected_ids(self):
        iids = self.tree.selection()
        list = []
        for iid in iids:
            list.append((iid, int(self.tree.item(iid, 'values')[0])))
        return list

    # Set up the treeview widgets
    def setup_widgets(self):
        self.container = ttk.Frame()

        self.tree = ttk.Treeview(columns = self.header, show = "headings", height = 50)
        vertical_scrollbar = ttk.Scrollbar(orient = "vertical",
                                       command = self.tree.yview)
        horizontal_scrollbar = ttk.Scrollbar(orient = "horizontal",
                                             command = self.tree.xview)

        self.tree.configure(yscrollcommand = vertical_scrollbar.set,
                            xscrollcommand = horizontal_scrollbar.set)

        self.tree.grid(column = 0, row = 0, sticky = 'nsew', in_ = self.container)

        vertical_scrollbar.grid(column = 1, row = 0, sticky = 'ns', in_ = self.container)
        horizontal_scrollbar.grid(column = 0, row = 1, sticky = 'ew', in_ = self.container)

        self.container.grid_columnconfigure(0, weight = 1)
        self.container.grid_rowconfigure(0, weight = 1)

    # Build the tree using the data
    def build_tree(self):
        for col in self.header:
            self.tree.heading(col, text = col.title(),
                              command = lambda c = col: self.sortby(self.tree, c, 0))

            self.tree.column(col,
                             width = tkFont.Font().measure(col.title()))

        for item in self.data:
            self.tree.insert('', 'end', values = item)

        sample = (6081, 2112, 30104665, 21, 21, 'DATA_02_02_Cow_6108.csv')

        for x, val in enumerate(sample):
            col_w = tkFont.Font().measure(val)
            if self.tree.column(self.header[x], width = None) < col_w:
                self.tree.column(self.header[x], width = col_w)

    # Sort the tree by a particular column
    def sortby(self, tree, col, descending):
        data = [(tree.set(child, col), child) for child in tree.get_children('')]
        data.sort(key = self.tupleToInt, reverse = descending)
        for x, item in enumerate(data):
            tree.move(item[1], '', x)
        tree.heading(col, command = lambda col = col: self.sortby(
            tree, col, int(not descending)))

    # Helper function to sort different data types
    def tupleToInt(self, tup):
        try:
            tupInt = (int(tup[0]), tup[1])
            return tupInt
        except:
            return tup


class Node:
    def __init__(self, info):
        self.info = info
        self.left = None
        self.right = None
        self.level = None

    def __str__(self):
        return str(self.info)

class BinarySearchTree:
    def __init__(self):
        self.root = None

    def create(self, val):
        if self.root == None:
            self.root = Node(val)
        else:
            current = self.root

            while True:
                if val < current.info:
                    if current.left:
                        current = current.left
                    else:
                        current.left = Node(val)
                        break
                elif val > current.info:
                    if current.right:
                        current = current.right
                    else:
                        current.right = Node(val)
                        break
                else:
                    break

"""
Node is defined as
self.left (the left child of the node)
self.right (the right child of the node)
self.info (the value of the node)
"""
def levelOrder(root):
    root.level = 0
    nodes = [root]
    traverse = []
    while len(nodes) > 0:
        node = nodes.pop(0)
        traverse.append(node.info)
        next_level = node.level + 1
        if node.left is not None:
            node.left.level = next_level
            nodes.append(node.left)
        if node.right is not None:
            node.right.level = next_level
            nodes.append(node.right)
    print(' '.join(map(str, traverse)))

tree = BinarySearchTree()
t = int(raw_input())

arr = list(map(int, raw_input().split()))

for i in xrange(t):
    tree.create(arr[i])

levelOrder(tree.root)


from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5.QtGui import QIcon, QPixmap, QFont

from EcgDetector import *
import pyscreenshot as ImageGrab

import sys
import tempfile
import subprocess
from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal

import pyttsx3

import time

import time
import traceback, sys

font_but = QtGui.QFont()
font_but.setFamily("Segoe UI Symbol")
font_but.setPointSize(15)
font_but.setWeight(100)

class PushBut1(QtWidgets.QPushButton):

    def __init__(self, parent=None):
        super(PushBut1, self).__init__(parent)
        self.setMouseTracking(True)
        self.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(1,255,255,100); color: rgba(0,190,255,255); border-style: solid;"
                           "border-radius: 3px; border-width: 0.5px; border-color: rgba(127,127,255,255);")

    def enterEvent(self, event):
        if self.isEnabled() is True:
            self.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(1,255,255,100); color: rgba(0,230,255,255);"
                               "border-style: solid; border-radius: 3px; border-width: 0.5px; border-color: rgba(0,230,255,255);")
        if self.isEnabled() is False:
            self.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(1,255,255,100); color: rgba(0,190,255,255); border-style: solid;"
                               "border-radius: 3px; border-width: 0.5px; border-color: rgba(127,127,255,255);")

    def leaveEvent(self, event):
        self.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(1,255,255,100); color: rgba(0,190,255,255); border-style: solid;"
                           "border-radius: 3px; border-width: 0.5px; border-color: rgba(127,127,255,255);")

class QThread1(QtCore.QThread):
    sig1 = pyqtSignal(str)
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
    def on_source(self, txt):
        #print(txt)'
        self.source_txt = txt
    def run(self):
        print("caledddddddddddddddddddddddddddddddddddddd")
        if self.source_txt:
            engine = pyttsx3.init()
            engine.setProperty('rate',120)
            engine.setProperty('volume', 100)
            engine.say(self.source_txt + " pattern is detetcted!")
            engine.runAndWait()

class PyQtApp(QtWidgets.QWidget):
    is_Start = False
    status_message = "Results..mmmmmmmmmmmmmmmmmmmmmmmmmmm"
    ecgDetector = None
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.setWindowTitle("RoBo Eye-Cardio Analyzer")
        self.setWindowIcon(QtGui.QIcon("Path/to/Your/image/file.png"))
        self.setFixedWidth(1200)
        self.setFixedHeight(350)
##        self.setMinimumWidth(resolution.width() / 3)
##        self.setMinimumHeight(resolution.height() / 1.5)
        self.setStyleSheet("QWidget {background-color: rgba(0,41,59,255);} QScrollBar:horizontal {width: 1px; height: 1px;"
                           "background-color: rgba(0,41,59,255);} QScrollBar:vertical {width: 1px; height: 1px;"
                           "background-color: rgba(0,41,59,255);}")

        self.thread1 = QThread1()
        self.thread1.sig1.connect(self.thread1.on_source)

        models_dir = 'E:\\models\\'
        self.ecgDetector = EcgDetector(models_dir)

        self.lbl2 = QtWidgets.QLabel(self)
        self.lbl2.setFixedWidth(630)
        self.lbl2.setFixedHeight(35)

##        self.lbl2.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(0,255,255,100); color: rgba(255,255, 255, 0);"
##                                 "border-style: solid; border-radius: 4px; border-width: 0.5px; border-color: rgba(0,140,255,255); ")

        font = QFont('Times', 12)
        self.lbl2.setFont(font)
        font.setWeight(95)
        #self.lbl2.setFontPointSize(12)
        self.lbl2.setText("Results...")
        self.lbl2.setFont(font)

        self.lbl2.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(0,255,255,0); color: rgba(255,255, 255, 0);"
                                 "border-style: solid; border-radius: 4px; border-width: 0.5px; border-color: rgba(0,255,255,0); ")

        self.lbl2.move(210,220)

        self.but1 = PushBut1(self)
        self.but1.setText("⯈")
        self.but1.setFixedWidth(72)
        self.but1.setFixedHeight(35)
        self.but1.setFont(font_but)

        self.but2 = PushBut1(self)
        self.but2.setText("⯀")
        self.but2.setFixedWidth(72)
        self.but2.setFixedHeight(35)
        self.but2.setFont(font_but)

        self.lb1 = QtWidgets.QLabel(self)
        self.lb1.setFixedWidth(800)
        self.lb1.setFixedHeight(160)
        self.lb1.move(40,40)
        self.lb1.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(0,255,255,100); color: rgba(0,190,255,255);"
                                 "border-style: solid; border-radius: 4px; border-width: 0.1px; border-color: rgba(0,140,255,255);")

##        self.textf.setStyleSheet("margin: 1px; padding: 7px;"
##                                 "border-style: solid; border-radius: 3px; border-width: 0.5px; border-color: rgba(0,140,255,255);")

        #self.grid1 = QtWidgets.QGridLayout()
##        self.grid1.addWidget(self.textf, 0, 0, 14, 13)
        self.but1.move(40,220)
        self.but2.move(120,220)
   #     self.grid1.addWidget(self.but2, 1, 28, 1, 1)
##        self.grid1.addWidget(self.but3, 2, 14, 1, 1)
        #self.grid1.addWidget(self.but4, 3, 14, 1, 1)
##        self.grid1.addWidget(self.but5, 4, 14, 1, 1)
##        self.grid1.addWidget(self.but6, 5, 14, 1, 1)
##        self.grid1.addWidget(self.but7, 6, 14, 1, 1)

      #  self.grid1.setContentsMargins(7, 7, 7, 7)
       # self.setLayout(self.grid1)
        self.but1.clicked.connect(self.on_but1)
        self.but2.clicked.connect(self.on_but2)

    def on_but1(self):
        #self.textf.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(1,255,217,100); color: rgba(0,190,255,255);"
                                 #"border-style: solid; border-radius: 3px; border-width: 0.5px; border-color: rgba(0,140,255,255);")
       # print("clicked")
        self.is_Start = True

        test_img = 'E:\\brady.png'
        models_dir = 'E:\\models\\'

##        self.lb1 = QtWidgets.QLabel(self)
##        self.lb1.setFixedWidth(900)
##        self.lb1.setFixedHeight(160)
        pixmap = QPixmap('E:\\models\\brady-full.png')
        self.lb1.setPixmap(pixmap)
        self.lb1.setAlignment(QtCore.Qt.AlignCenter)
        #self.grid1.addWidget(self.lb1, 0, 0, 1, 1)
##        self.lb1.move(150,50)
        i = 0;
        currentEvent = None
        while(self.is_Start):
            #print("inside while")
            i = i+ 1;
           # print(i);

            im = ImageGrab.grab(bbox = (100, 140, 900,300));
            im.save(test_img);
            rslt = self.ecgDetector.compare(test_img)
            if (rslt != currentEvent):
                self.thread1.sig1.emit(rslt)
                self.thread1.start()
                self.eventLabelUpdate(rslt)
            currentEvent = rslt
           # print(self)
##            if rslt:
##                self.thread1 = QThread1()
##                self.thread1.sig1.connect(self.thread1.on_source)
##                self.thread1.sig1.emit(rslt)
##                self.thread1.start()

            pixmap = QPixmap('E:\\res.png')
            self.lb1.setPixmap(pixmap)
            QApplication.processEvents()

        ##self.initUI();

    def on_but2(self):
       self.is_Start = False

    def eventLabelUpdate(self,event_label):
        #print ("hello update")
        if event_label:
            self.lbl2.setText(event_label + " pattern detected!!!")
            self.lbl2.setStyleSheet("margin: 1px; background-color: rgba(255,0,0,1); color: rgba(255,255,255,1);"
                                     "border-style: solid; border-radius: 4px; border-width: 0.1px; border-color: rgba(0,140,255,255);")
        else:
             self.lbl2.setText("")
             self.lbl2.setStyleSheet("margin: 1px; padding: 7px; background-color: rgba(255,0,0,0); color: rgba(255,255, 255, 0);"
                                 "border-style: solid; border-radius: 4px; border-width: 0.5px; border-color: rgba(255,0,0,0); ")

    def initUI(self):
        print("clicked")

        test_img = 'E:\\brady.png'
        models_dir = 'E:\\models\\'

##        self.lb1 = QtWidgets.QLabel(self)
##        self.lb1.setFixedWidth(900)
##        self.lb1.setFixedHeight(160)
        pixmap = QPixmap('E:\\models\\brady-full.png')
        self.lb1.setPixmap(pixmap)
        self.lb1.setAlignment(QtCore.Qt.AlignCenter)
        #self.grid1.addWidget(self.lb1, 0, 0, 1, 1)
##        self.lb1.move(150,50)
        i = 0;

        while(self.is_Start):
            print("inside while")
            i = i+ 1;
            print(i);

            im = ImageGrab.grab(bbox = (100, 140, 900,300));
            im.save(test_img);
            rslt = compare(test_img, models_dir)
            print(self)
##            if rslt:
##                self.thread1 = QThread1()
##                self.thread1.sig1.connect(self.thread1.on_source)
##                self.thread1.sig1.emit(rslt)
##                self.thread1.start()

            pixmap = QPixmap('E:\\res.png')
            self.lb1.setPixmap(pixmap)
            QApplication.processEvents()

    def updateEventInfo(self):
        print("Do action here!")

        print("Do action here!")

if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    desktop = QtWidgets.QApplication.desktop()
    resolution = desktop.availableGeometry()
    myapp = PyQtApp()
    myapp.show()

##    myapp.threadpool = QThreadPool()
##    print("Multithreading with maximum %d threads" % myapp.threadpool.maxThreadCount())
##

    #myapp.setWindowOpacity(0.95)
    #myapp.show()
    #Smyapp.move(resolution.center() - myapp.rect().center())
    sys.exit(app.exec_())
else:
    desktop = QtWidgets.QApplication.desktop()
    resolution = desktop.availableGeometry()


import torch
import torchaudio
import torchaudio.functional
import torchaudio.transforms
import torchvision.transforms
from scipy import signal

import math
import numpy as np
import sklearn

import librosa
from typing import List

def load_wav(path:str) -> List[float]:
    '''
    returned wav is an array of floats, not a tensor of shape (1, #points)
    '''
    wav, sr = librosa.load(path, sr=44100)
    return wav

def resize_function(output_shape=(128,512)):
    return torchvision.transforms.Resize(size=output_shape, antialias=False)

def calculate_melsp(x, n_fft=1024, hop_length=128):
    stft = np.abs(librosa.stft(x, n_fft=n_fft, hop_length=hop_length))**2
    log_stft = librosa.power_to_db(stft)
    melsp = librosa.feature.melspectrogram(S=log_stft,n_mels=128)
    return melsp

def to_reshaped_tensor(image):
    image = torch.tensor(image)
    return image.unsqueeze(0)

def non_normalised_data_generator(
    paths: List[str],
    labels: List[int],
    image_preprocess_fn=resize_function(),
    mel_transform_fn=calculate_melsp,
    wav_max_amplitude:float=0.5
):
    for (path, label) in zip(paths, labels):
        wav = load_wav(path=path)
        db_mel_spec = mel_transform_fn(wav)
        db_mel_spec = to_reshaped_tensor(db_mel_spec)
        yield image_preprocess_fn(db_mel_spec), label

def normalise_image(image, mean, std, eps=1e-6):
    """
    Return a normalised image that has mean 0 and standard deviation 1.

    Args:
        image: A pytorch tensor
        mean: Precalculated mean of all images
        std: Precalculated standard deviation of all images

    Returns:
        The normalised image (pytorch tensor)
    """
    return (image - mean) / (std + eps)

def normalised_data_generator(generator, mean, std):
    """
    Return a generator with normalised images.

    Args:
        generator: A generator outputing (non-normalised image, label)
        mean: Precalculated mean of all non-normalised images
        std: Precalculated standard deviation of all non-normalised images

    Returns:
        generator outputing (normalised image with mean 0 and std 1, label)
    """
    for image, label in generator:
        yield normalise_image(image, mean, std), label

def get_stats_and_class_weights_of_non_normalised_data_gen(data_gen, image_width_height):
    total_sum = torch.Tensor([0.0])
    total_squared_sum = torch.Tensor([0.0])
    num_of_images: int = 0
    labels = []

    for image, label in data_gen:
        labels.append(label)
        num_of_images += 1
        total_sum += image.sum()
        total_squared_sum += (image ** 2).sum()

    num_of_data = num_of_images * image_width_height[0] * image_width_height[1]

    mean = total_sum / num_of_data
    variance = total_squared_sum / num_of_data - mean ** 2
    std = torch.sqrt(variance)
    class_weights = sklearn.utils.class_weight.compute_class_weight(class_weight='balanced', classes=np.unique(labels), y=labels)
    return mean, std, class_weights

def calculate_num_of_labels(gen):
    result_dict = {}
    for _, label in gen:
        if label not in result_dict:
            result_dict[label] = 1
        else:
            result_dict[label] += 1
    return result_dict


"""
ClimateDataPolling.py
Created:
@author: Will Rhodes

"""

from Helpers.GraphFxts import *
from Helpers.NumericalFxts import *
from Models.GeographicLocation import *
import csv
import numpy as np

def start():
    print("Starting Program")
    datafile = './Data/NOAA_Monthly_Climatological_Summary_2010.csv'
    datalist = GetGeographicDataList(datafile)
    print('2nd pass stores {0} items'.format(len(datalist)))
    datalist = narrowStationsByClimateBounds(datalist)
    print('3rd pass stores {0} items'.format(len(datalist)))
    #####################################################################
    #print([[x.Name,x.Climate.GetAvgDailyHighWinter()] for x in datalist])
    #####################################################################
    PlotGeographicDataList(datalist)
    #####################################################################
    if 0==1:
        testlocation = filterByName(datalist,'PHILADELPHIA')[0]
        print(testlocation.toString())
        print(testlocation.Climate.toString())
        ScatterPlot(list(range(1,13)),testlocation.Climate.MeanTempMonthly,errors=[x/2.0 for x in testlocation.Climate.MeanDiurnalRangeMonthly], title=testlocation.Name + ' Mean Temp with Diurnal Variance', xlabel='Month',ylabel='Degrees (F)')
        #ScatterPlot(list(range(1,13)),testlocation.Climate.MaxTempMonthly,errors=[x/2.0 for x in testlocation.Climate.MeanStdDevMonthly], title=testlocation.Name + ' Max Temp with Std Dev', xlabel='Month',ylabel='Degrees (F)')
    #####################################################################

def GetGeographicDataList(datafile):
    GeographicDataList = []
    with open(datafile, newline='') as csvfile:
        has_header = csv.Sniffer().has_header(csvfile.read(1024))
        csvfile.seek(0)  # rewind
        csvReader = csv.reader(csvfile)
        if has_header:
            next(csvReader)
        currentstation = ""
        stationdata = []
        for row in csvReader:
            if row[0] != currentstation:
                #we changed stations
                if len(stationdata) > 0:
                    GeographicDataList.append(GeographicLocation(stationdata))
                stationdata = []
            currentstation = row[0]
            stationdata.append(row)
    print('First pass stores {0} items'.format(len(GeographicDataList)))
    return [x for x in GeographicDataList if x.IsValid()]

def PlotGeographicDataList(datalist):
    xs = []
    ys = []
    zs = []
    for item in datalist:
        xs.append(item.Longitude)
        ys.append(item.Latitude)
        zs.append(item.Climate.MeanTempAvg)
        #print(zs)
    AmericaMapScatter(xs,ys,zs)

def narrowStationsByClimateBounds(datalist):
    idealThresholdMinT = 20
    idealThresholdMaxT = 90
    idealRangeSummerMeanT = (65,84)
    idealRangeWinterMeanT = (46,80)
    eaternOnly = 1==1#float(location.Longitude) > -95.0
    narrowedList = []
    for location in datalist:
        if eaternOnly and location.Climate.MinTemp >= idealThresholdMinT and location.Climate.MaxTemp <= idealThresholdMaxT and (idealRangeSummerMeanT[0] <= location.Climate.GetAvgDailyHighSummer() <= idealRangeSummerMeanT[1]) and (idealRangeWinterMeanT[0] <= location.Climate.GetAvgDailyHighWinter() <= idealRangeWinterMeanT[1]):
            narrowedList.append(location)
    return narrowedList

def filterByName(datalist, state):
    return [x for x in datalist if state in x.Name]
if  __name__ =='__main__':start()


class Game:
    primitive_position = [0]
    possible_moves = []

    def primitive_positions(self, position):
        if position in self.primitive_position:
            return True
        else:
            return False

    def DoMove(self, position, move):
        if not move in self.possible_moves or position - move < 0:
            return "illegal move"

        if self.primitive_positions(position):
            return "lose"

        return position - move

    def GenerateMoves(self, position):
        "returns a list of possible moves"
        ret_moves = []
        for move in self.possible_moves:
            if self.DoMove(position, move) != "illegal move" and type(self.DoMove(position, move)) != "String":
                ret_moves.append(move)

        return ret_moves

    def PrimitiveValue(self, position):
        if self.primitive_positions(position):
            return "lose"
        else:
            return "undecided"


import os
import subprocess

import click
import redis
from rq import Connection, Worker

from demo import create_app, db
from demo.api.users.models import User

env = os.getenv("FLASK_ENV") or "development"
print(f"Active environment: * {env} *")
app = create_app(env)

@app.cli.command()
@click.option("-c", "--cover", is_flag=True, help="Run with coverage report.")
def test(cover):
    """Run the tests with or without coverage."""
    if cover:
        cmd = "pytest -p no:warnings --cov=demo/ --cov-report html"
        print("Running tests with coverage.")
    else:
        cmd = "pytest -p no:warnings"
        print("Running tests.")
    return subprocess.run(cmd, shell=True)

@app.cli.command()
def recreate_db():
    """Recreate database."""
    print("Recreating database.")
    db.drop_all()
    db.create_all()
    db.session.commit()

@app.cli.command()
def run_worker():
    redis_url = app.config["REDIS_URL"]
    redis_connection = redis.from_url(redis_url)
    with Connection(redis_connection):
        worker = Worker(app.config["QUEUE"])
        worker.work()

@app.cli.command()
def seed_db():
    """Seeds the database."""
    print("Seeding database.")
    db.session.add(User(username="ben", email="ben@email.com"))
    db.session.add(User(username="benranderson", email="ben@email.org"))
    db.session.commit()