""" Attempt to set up some basic Python interface for Alvium camera
Based off Vimba python examples - synchronous & asynchronous grab
Will start with a synchronous implementation\
IH 5/1/23
"""

import sys
import cv2
from typing import Optional
from vimba import *


# copied but modified to not print help text
def abort(reason: str, return_code: int = 1):
    print(reason + '\n')

    sys.exit(return_code)


# copied from sample code; this function allows a camera id to be passed, e.g. by user argument
# but not including the code to read a camera ID as user argument
# since we'll only have one camera and easier to autodetect
def get_camera(camera_id: Optional[str]) -> Camera:
    with Vimba.get_instance() as vimba:
        if camera_id:
            try:
                return vimba.get_camera_by_id(camera_id)

            except VimbaCameraError:
                abort('Failed to access Camera \'{}\'. Abort.'.format(camera_id))

        else:
            cams = vimba.get_all_cameras()
            if not cams:
                abort('No Cameras accessible. Abort.')

            return cams[0]
        
# below copied from aysnchronous_grab_opencv
# some stuff for setting it up to be viewable, i guess
# take out the GigE thing
def setup_camera(cam: Camera):
    with cam:

        # configure - asynchronous grab opencv sets exposure to auto, here we configure for fixed, controllable exposur
        cam.ExposureMode.set('Timed') # need this bc some other programs (or below) may turn this off
        cam.ExposureAuto.set('Off')
        cam.ExposureTime.set(10000)
        
        """
        try:
            cam.ExposureMode.set('Timed') # need this bc some other programs (or below) may turn this off
        except (AttributeError, VimbaFeatureError):
            pass
    
        try:
            cam.ExposureAuto.set('Off')
        except (AttributeError, VimbaFeatureError):
            pass
    
        try:
            cam.ExposureTime.set(10000) # in us - this is 10 ms
        except (AttributeError, VimbaFeatureError):
            pass
            
        # Enable white balancing if camera supports it
        # ours shouldn't since it's monochrome
        try:
            cam.BalanceWhiteAuto.set('Continuous')

        except (AttributeError, VimbaFeatureError):
            pass
        """

        # Query available, open_cv compatible pixel formats
        # prefer color formats over monochrome formats
        cv_fmts = intersect_pixel_formats(cam.get_pixel_formats(), OPENCV_PIXEL_FORMATS)
        color_fmts = intersect_pixel_formats(cv_fmts, COLOR_PIXEL_FORMATS)

        if color_fmts:
            cam.set_pixel_format(color_fmts[0])

        else:
            mono_fmts = intersect_pixel_formats(cv_fmts, MONO_PIXEL_FORMATS)

            if mono_fmts:
                cam.set_pixel_format(mono_fmts[0])

            else:
                abort('Camera does not support a OpenCV compatible format natively. Abort.')


def main():
    # cam_id = parse_args() - remove this

    ENTER_KEY_CODE = 13

    with Vimba.get_instance():
        with get_camera(None) as cam:
            setup_camera(cam)

            # test here - configure to trigger on pin
            # use function generator for test
            #cam.ExposureMode.set('TriggerWidth')
            #cam.LineSelector.set('Line0')
            #cam.LineMode.set('Input')
            #cam.TriggerSelector.set('FrameStart')
            #cam.TriggerActivation.set('LevelHigh')
            #cam.TriggerSource.set('Line0')
            #cam.TriggerMode.set('On')

            i = 1

            # Acquire endless frames with a custom timeout (default is 2000ms) per frame acquisition.
            for frame in cam.get_frame_generator(limit=None, timeout_ms=3000):
                print('Got {}'.format(frame), flush=True)

                msg = 'Frames from \'{}\'.'
                cv2.imshow(msg.format(cam.get_name()), frame.as_opencv_image())
                key = cv2.waitKey(1) 
                # waitKey(1) waits for 1ms; asynchronous_grab_opencv calls this before imshow to check for Enter, 
                # but examples online call it after
                # code doesn't work without it
                # we'll also check for an Enter to close the window
                if key == ENTER_KEY_CODE:
                    cv2.destroyWindow()
                    pass

                i += 1
                cam.ExposureTime.set(10000 * (i%10+1))


if __name__ == '__main__':
    main()