# Copyright 2023 Jens Flemming, https://www.fh-zwickau.de/~jef19jdw
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# Full GNU General Public License text:
#     https://www.gnu.org/licenses/gpl-3.0.txt. 


import cv2
import threading
import time

class CVCamShot:
    ''' class for providing snapshots from an OpenCV video stream '''

   
    def __init__(self, device=0, width=1280, height=720):
        ''' device is the cam identifier (0, 1, 2,...) '''
       
        self._snapshot = None    # holds last image provided to user

        # initialize cam
        self.cam = cv2.VideoCapture(device)
        if not self.cam.isOpened():
            raise Exception(f'Camera device {device} not available!')
            
        # set resolution
        self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, width)
        self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

        # start capture thread
        self._take_snapshot_event = threading.Event()
        self._capture_stop_event = threading.Event()
        self._capture_thread = threading.Thread(target=self._threaded_capture)
        self._capture_thread.start()


    def _threaded_capture(self):
        ''' get frames from cam '''
        
        while not self._capture_stop_event.is_set():
            if self._take_snapshot_event.is_set():    # take snapshot
                got_frame, frame = self.cam.read()
                if got_frame:
                    self._snapshot = frame[:, :, ::-1].copy()
                else:
                    self._capture_stop_event.set()
                    raise Exception('No more frames from cam!')
                self._take_snapshot_event.clear()    # signal snapshot taken
            else:    # skip frames (no snapshot requested)
                time.sleep(0.02)
                self.cam.grab()
        
       
    def get(self):
        ''' update and return snapshot '''
        
        self._take_snapshot_event.set()
        while self._take_snapshot_event.is_set():
            pass
        
        return self._snapshot


    def stop(self):
        ''' stop streaming (no more snapshot updates) '''

        # stop thread        
        self._capture_stop_event.set()
        self._capture_thread.join()
        
        # release cam
        self.cam.release()
