# -*- coding: utf-8 -*-
"""
Created on Sun Sep 29 18:21:49 2019

@author: qchat
"""
from typing import Union
import time
import threading
from queue import Queue

import numpy as np
import pandas as pd
from qtpy import QtCore, QtWidgets

from ...variables import Variable
from ...elements import Variable as Variable_og


class MonitorManager:

    def __init__(self, gui: QtWidgets.QMainWindow):

        self.gui = gui

        # Configure a new thread
        self.thread = MonitorThread(self.gui.variable, self.gui.queue)
        self.thread.errorSignal.connect(self.error)

    def error(self, error: Exception):
        """ This function is called when the errorSignal of the thread is raised.
        It update the pause button and displays the error in the GUI """
        self.gui.pauseButton.setText('Resume')
        self.gui.setStatus(f'Error: {error} ', 10000, False)

    def start(self):
        """ This function start the thread """
        self.thread.start()

    def setDelay(self, value):
        """ Set the delay in the thread """
        self.thread.delay = value

    def getDelay(self):
        """ Returns the current delay of the thread """
        return self.thread.delay

    def isPaused(self):
        """ This function returns whether the thread is paused or not """
        return self.thread.pauseFlag.is_set()

    def resume(self):
        """ This function resume the monitoring """
        self.thread.pauseFlag.clear()

    def pause(self):
        """ This function pause the monitoring """
        self.thread.pauseFlag.set()

    def close(self):
        """ This function stops the thread and wait for its complete deletion """
        self.resume()
        self.thread.stopFlag.set()
        self.thread.wait()


class MonitorThread(QtCore.QThread):
    """ This thread class is dedicated to read the variable, and send its data to GUI through a queue """

    errorSignal = QtCore.Signal(object)

    def __init__(self, variable: Union[Variable, Variable_og], queue: Queue):

        super().__init__()
        self.variable = variable
        self.queue = queue

        self.pauseFlag = threading.Event()
        self.stopFlag = threading.Event()

        self.delay = 0

    def run(self):

        t_ini = time.time()
        pauseLength = 0
        pauseStartedTime = None

        while not self.stopFlag.is_set():
            # If the thread just resume, take into account the delay it has been paused
            if pauseStartedTime is not None:
                pauseLength += time.time() - pauseStartedTime
                pauseStartedTime = None

            # Time measure
            now = time.time() - t_ini - pauseLength

            try:
                # Measure variable
                value = self.variable()

                # Check type
                if not isinstance(value, (np.ndarray, pd.DataFrame)):  # should not float(array) because if 0D convert to float and loose information on type
                    try:
                        value = float(value)
                    except TypeError:
                        assert hasattr(value, "shape"), "If data is not a float, should be an array or a dataframe"

                # Send signal new data
                self.queue.put([now, value])

            except Exception as e:
                self.errorSignal.emit(e)
                self.pauseFlag.set()

            # If not the thread may be too fast
            time.sleep(self.delay)

            # pause
            while self.pauseFlag.is_set():
                if pauseStartedTime is None:
                    pauseStartedTime = time.time()
                time.sleep(0.1)
