##  @package VideoWindow
#   A live video preview window that can be cycled between cameras.
#
#   @author     Mark Zvilius
#   @date       10-Feb-2020
#   @copyright  Virtual Reality Technology Underwater Limited (VRTUL)
#
import sys
from omxplayer.player import OMXPlayer

##
#   A video window.
#
#   This uses OMXPlayer as the display mechanism. OMXPlayer uses the GPU to minimize
#   CPU load. The CameraGroup object is used to cycle through the cameras.
#
class VideoWindow:
    ##
    #   Constructor.
    #
    #   @param  self        Object pointer.
    #   @param  size        (x,y) Size of window in pixels.
    #   @param  position    (x,y) Screen position of upper left corner in pixels.
    #   @param  cameras     CameraGroup instance
    #
    def __init__(self, size, position, cameras):
        self._rtsp_url = ''
        self._player = None
        self._ul_x = position[0]
        self._ul_y = position[1]
        self._lr_x = self._ul_x + size[0]
        self._lr_y = self._ul_y + size[1]
        self._streaming_args = []
        self._cams = cameras
        self._index = -1                    # camera index

    ##
    #   Set OMXPlayer arguments.
    #
    #   Set arguments that are appropriate for the camera or the application.
    #
    #   @param  self        Object pointer.
    #   @param  args        (list) Arguments.
    #
    #   @remark Anything that would be separated by a space on the command line
    #           must be a separate list item for "args".
    #
    def set_streaming_args(self, args):
        self._streaming_args = args

    ##
    #   If the video window is active.
    #
    #   @param  self        Object pointer.
    #   @return             True if video window is active.
    #
    def is_running(self):
        return self._player is not None

    ##
    #   Get current camera instance.
    #
    #   @param  self        Object pointer.
    #   @return Camera object.
    #
    def get_camera(self):
        return self._cams.cameras[self._index]

    ##
    #   Start the video window or switch to a different camera stream.
    #
    #   @param  self        Object pointer.
    #   @param  index       Camera index.
    #   @return             Camera ID string.
    #
    #   TODO: Get URL from E2Camera or some kind of DI instead of hard-coded here.
    #
    def go(self, index):
        self._cams.validate_index(index)
        cam = self._cams.cameras[index]
        self._rtsp_url = f'rtsp://{cam.ip}/live_stream'
        self.stop()
        self._index = index
        self._start()
        print(f'cam {cam.id} idx={self._index}')
        return cam.id

    ##
    #   Restart video window.
    #
    #   If video window is running, it will be stopped and restarted. If it was
    #   previously stopped, it will be started with the same camera stream.
    #
    #   @param  self        Object pointer.
    #   @return             Camera ID string.
    #
    def restart(self):
        return self.go(self._index)

    ##
    #   Switch video window to show previous camera in the list, with wrapping.
    #
    #   @param  self        Object pointer.
    #   @return             Camera ID string.
    #   
    def command_camera_down(self):
        # Index of -1 means player isn't running. This could happen if _start()
        # raised an exception. In this case try starting the master camera.
        if self._index == -1:
            self._index = 0
        else:
            self._index = self._cams.wrap_index_decrement(self._index)
        return self.go(self._index)

    ##
    #   Switch video window to show next camera in the list, with wrapping.
    #
    #   @param  self        Object pointer.
    #   @return             Camera ID string.
    #   
    def command_camera_up(self):
        # Index of -1 means player isn't running. This could happen if _start()
        # raised an exception. In this case try starting the master camera.
        if self._index == -1:
            self._index = 0
        else:
            self._index = self._cams.wrap_index_increment(self._index)
        return self.go(self._index)

    ##
    #   Start the video window.
    #   (Private method.)
    #
    #   @param  self        Object pointer.
    #   @throws AssertionException  Video window is already running.
    #
    def _start(self):
        assert(not self.is_running())

        try:
            self._player = OMXPlayer(self._rtsp_url, args=self._streaming_args)
            self._player.set_video_pos(self._ul_x, self._ul_y, self._lr_x, self._lr_y)
            self._player.set_aspect_mode('letterbox')
        except Exception as err:
            self._player = None
            self._index = -1
            print('exception:', err)

    ##
    #   Stop the video window.
    #
    #   @param  self        Object pointer.
    #
    #   @remark This does not change _index, which allows restart() to be
    #           called later, in order to restart using the same camera.
    #
    def stop(self):
        if self.is_running():
            self._player.quit()
            self._player = None


##  Main loop.
def main(video):
    while 1:
        for camera_ip in CAMERAS:
            input('ENTER for next camera, CTRL-D to exit\n')
            print('connecting', camera_ip)
            url = f'rtsp://{camera_ip}/live_stream'
            print(url)
            video.go(url)

        input('ENTER to stop video, CTRL-D to exit\n')
        print('disconnecting')
        video.stop()


if __name__ == '__main__':
    video = VideoWindow(size=SIZE, position=POS)
    video.set_streaming_args(E2_STREAMING_ARGS)
    try:
        main(video)
    except EOFError as err:
        print('exiting')
    finally:
        video.stop()
