import asyncio
import re
import time
from threading import Thread

# import cv2
import numpy as np
from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtSlot, QThread, pyqtProperty
# from PyQt5.QtGui import QImage
# from PyQt5 import QtGui

from . import vision_service


class VideoStreamer(QObject):
    newImage = pyqtSignal(object, int, int)
    newRawImage = pyqtSignal(object)
    reconnectInitiated = pyqtSignal()
    reconnect = pyqtSignal()
    camChangeStart = pyqtSignal()
    camChangeEnd = pyqtSignal(int)
    isCamChangingChanged = pyqtSignal(bool)
    camReconnected = pyqtSignal(int)
    beginOpenVideoCamera = pyqtSignal(str)

    def __init__(self, parent: QObject = None):
        super(QObject, self).__init__(parent)
        self.__is_changing_camera = False
        self.__stopped = False
        self.__grabbed = False
        self.__is_reconnecting = False

        self.__frame = np.array([])
        self.__cap = None
        self.__tmp_cap = None
        self.__rtsp_link = ''
        self.__tmp_rtsp_link = ''
        self.ip = ''

        self.tUpdate = None
        self.reconnectTimer = None
        self.t = None

        

    @pyqtProperty(bool, notify=isCamChangingChanged)
    def isChangingCamera(self):
        return self.__is_changing_camera

    @pyqtSlot()
    def beginReconnect(self):
        print('RECONNECTING')
        self.stopVideoCamera()
        self.__is_reconnecting = True
        # QTimer.singleShot(5000, self.reconnectVideo)
        t = Thread(target=self.reconnectVideo, args=())
        t.daemon = True
        t.start()

    # @pyqtSlot()
    def reconnectVideo(self):
        rtsp_link = self.__tmp_rtsp_link
        print(f'reconnecting to {rtsp_link}')
        self.stopVideoCamera()
        while True:
            self.reconnect.emit()
            cap = vision_service.vision_service(rtsp_link)
            cap.start_framing()
            time.sleep(5)
            if cap.get_is_framing():
                print('cam is online')
                self.camChangeEnd.emit(True)

                time.sleep(0.1)

                self.__cap = None

                fps = cap.get_fps()
                if fps == 0:
                    if not cap.get_is_framing():
                        return False
                    else:
                        fps = 60

                self.__stopped = True
                self.tUpdate.stop()
                self.reconnectTimer.stop()

                self.__cap = cap
                self.__rtsp_link = rtsp_link

                self.t = Thread(target=self.update, args=())
                self.t.daemon = True
                self.camReconnected.emit(int(1000 // fps))
                self.__stopped = False

                if not self.t.is_alive():
                    self.t.start()

                self.__is_reconnecting = False
                return True

    def update(self):
        while True:
            if self.__stopped is True:
                print('self.__stopped is True:')
                self.__frame = None
                break

            if not self.__cap:
                print('not self.__cap')
                time.sleep(0.1)
                continue

            if self.__cap.get_is_framing():
                self.__frame = self.__cap.get_frame()
                time.sleep(0.066)
            else:
                time.sleep(0.1)
                if not self.__is_reconnecting:
                    print('not self.__is_reconnecting')
                    self.__is_reconnecting = True
                    self.reconnectInitiated.emit()  # TODO send error to client
        #     if self.__frame is False:
        #         print('[Exiting] No more frames to read')
        #         self.__stopped = True
        #         break
        # self.__cap.release()

    @pyqtSlot()
    def run(self) -> None:
        print('starting QThread')
        self.tUpdate = QTimer(self)
        self.reconnectTimer = QTimer(self)
        # self.tUpdate.moveToThread(self)
        # self.reconnectTimer.moveToThread(self)
        self.t = Thread(target=self.update, args=())
        self.t.daemon = True  # daemon threads run in background

        self.tUpdate.timeout.connect(self.streamVideo)
        self.reconnectTimer.timeout.connect(self.reconnectVideo)
        self.reconnectInitiated.connect(self.beginReconnect)
        self.camReconnected.connect(self.tUpdate.start)
        self.beginOpenVideoCamera.connect(self.openVideoCamera)
        # self.exec_()

    @pyqtSlot()
    def streamVideo(self):
        pass
        # if (self.__frame is not None) and self.__frame.size != 0:
            # height, width, channel = self.__frame.shape
            # bytesPerLine = 3 * width
            # qImg = QImage(self.__frame.data, width, height, bytesPerLine, QImage.Format_BGR888)
            # self.newRawImage.emit(self.__frame)
            # self.newImage.emit(qImg, width, height)

    def get_frame(self):
        return self.__frame

    @pyqtSlot(result=bool)
    def connectCam(self):
        print('CONNECTING CAM')
        fps = self.__tmp_cap.get_fps()
        print('NEW CAP FPS:', fps)
        if fps == 0:
            if not self.__tmp_cap.get_is_framing():
                return False
            else:
                fps = 60
        self.__stopped = True
        self.tUpdate.stop()
        self.reconnectTimer.stop()

        self.__rtsp_link = self.__tmp_rtsp_link
        self.__cap = self.__tmp_cap
        self.ip  = re.findall("(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", self.__rtsp_link)[0]

        self.t = Thread(target=self.update, args=())
        self.t.daemon = True
        self.tUpdate.start(int(1000 // fps))
        self.__stopped = False
        if not self.t.is_alive():
            self.t.start()
        self.__is_changing_camera = False
        self.isCamChangingChanged.emit(self.__is_changing_camera)
        return True

    @pyqtSlot(result=bool)
    def checkCam(self):
        print('CHECKING CAMERA')
        if self.__tmp_cap.get_is_framing():
            print('camera is framing')
            self.stopVideoCamera()
            self.camChangeEnd.emit(True)
            QTimer.singleShot(100, self.connectCam)
            return True
        else:
            print('camera is not framing')
            self.camChangeEnd.emit(False)
            self.__is_changing_camera = False
            self.isCamChangingChanged.emit(self.__is_changing_camera)
            return False

    @pyqtSlot(str, result=bool)
    def changeCamera(self, rtsp_link):
        self.__is_changing_camera = True
        self.isCamChangingChanged.emit(self.__is_changing_camera)
        self.__tmp_rtsp_link = rtsp_link
        self.__tmp_cap = vision_service.vision_service(rtsp_link)
        self.__tmp_cap.start_framing()
        QTimer.singleShot(5000, self.checkCam)
        self.camChangeStart.emit()
        return True
        # except:
        #     print('cannot stop framing')
        #     return False

    @pyqtProperty(str)
    def rtspLink(self):
        return self.__rtsp_link

    @pyqtSlot(result=bool)
    @pyqtSlot(str, result=bool)
    def openVideoCamera(self, rtsp_link: str = None):
        if self.__cap is None:
            if rtsp_link is not None:
                self.__tmp_rtsp_link = rtsp_link
            print('rtsp_link:', self.__tmp_rtsp_link)
            self.__tmp_cap = vision_service.vision_service(self.__tmp_rtsp_link)
            self.__tmp_cap.start_framing()
            QTimer.singleShot(5000, self.checkCam)
            self.camChangeStart.emit()
            return True
        return False

    @pyqtSlot()
    def clearVideoCamera(self):
        self.__cap = None
        self.__rtsp_link = ''
        print('clearVideoCamera finished')

    @pyqtSlot(result=bool)
    def stopVideoCamera(self):
        self.__stopped = True
        self.t = Thread(target=self.update, args=())
        self.t.daemon = True
        print('stopping video camera')
        self.tUpdate.stop()
        self.reconnectTimer.stop()
        if not self.__is_reconnecting:
            QTimer.singleShot(100, self.clearVideoCamera)
        try:
            if self.__cap:
                self.__cap.stop_framing()
            return True
        except Exception as e:
            print('cannot stop framing', e)
            return False

    def __del__(self):
        # self.__cap.release()
        try:
            self.__cap.stop_framing()
        except:
            print('cannot stop framing')
        self.tUpdate.stop()
        self.__stopped = True