// NOTE, in this code I am purposely ignoring error return codes for the sake of clarity while
// walking through the code.  Normally, you should check the return codes for errors, as this
// will greatly help to isolate bugs.
static int
init_camera()
{
    camera_error_t err;
    unsigned int num;
    unsigned int i;
    camera_unit_t cams[CAMERA_UNIT_NUM_UNITS];
    camera_unit_t unit;
    // here are 2 ways to determine which cameras are available on a given device...
#if 0
    // METHOD 1
    // inventory the available camera units
    // NOTE: to just find the number of available cameras: camera_get_cameras(0, &num, NULL);

    camera_get_supported_cameras(CAMERA_UNIT_NUM_UNITS,
                                 &num,
                                 cams);
    for (i=0; i<num; i++) {
        fprintf(stderr, "found camera unit %d\n", cams[i]);
    }
#else
    // METHOD 2
    // inventory cameras which support a given feature set - in this case PHOTO & BURST

    camera_feature_t features[] = { CAMERA_FEATURE_PHOTO, CAMERA_FEATURE_BURST };
    camera_unit_t next = CAMERA_UNIT_NONE;
    num = 0;
	// note that this is an iterating function call which returns only one "next" unit at a time
	while (camera_find_capable(features,
							   sizeof(features)/sizeof(*features),
							   next,
							   &next) == CAMERA_EOK) {
        cams[num++] = next;
        fprintf(stderr, "camera unit %d supports the required features\n", next);
    }
#endif

    // open the first camera found
    unit = cams[0];
    fprintf(stderr, "selecting camera unit %d\n", unit);
    err = camera_open(unit,
                      CAMERA_MODE_RW | CAMERA_MODE_ROLL,
                      &handle);
    if (err != CAMERA_EOK) {
        fprintf(stderr, "camera_open() failed: %d\n", err);
        return err;
    }

    // This is the minimal required configuration for a viewfinder.
    // NOTE: we need to enable burst mode when starting the viewfinder.
    // the maximum burst framerate is 15fps.  don't trust me?  you can query
    // camera_get_photo_vf_framerates() to determine the capabilities.
    err = camera_set_photovf_property(handle,
                                      CAMERA_IMGPROP_WIN_GROUPID, vf_group,
                                      CAMERA_IMGPROP_WIN_ID, "my_viewfinder",
                                      CAMERA_IMGPROP_BURSTMODE, 1,
                                      CAMERA_IMGPROP_FRAMERATE, (double)15.0);  // max for burst is 15fps.
    if (err != CAMERA_EOK) {
        // NOTE: if you need to narrow down which setting is causing an error,
        // consider breaking the above command down into multiple calls.
        // be aware that some values must be changed together though (eg. height & width)
        // in order to pass range-checking
        fprintf(stderr, "camera_set_photovf_property() failed: %d\n", err);
    } else {

        // a valid photovf and photo configuration are required before starting
        // the viewfinder.
        // the defaults for both will always be sane, however if certain properties
        // are changed in one, they may need to be changed in the other.
        // resolution is one such example (aspect ratios must match).
        // here is an example configuration for photo properties - setting up the burst divisor.
        // this is only valid in burst viewfinder mode, and will cause the camera service to
        // only deliver every 3rd frame.  (note that fractional rates are allowed).
        // why provide a divisor instead of just setting 5fps?
        camera_set_photo_property(handle,
                                  CAMERA_IMGPROP_BURSTDIVISOR, (double)3.0);  // DOUBLE!

        // callbacks are optional, however status callback is useful for detecting asynchronous events
        // unless your application requires processing of viewfinder frame data, don't bother with
        // a viewfinder callback, as it incurs some additional ipc overhead.  remember, the viewfinder
        // window itself is already rendered by the camera service, not your app.
        // NOTE: we are passing main_bps_chid as the void* arg which will then
        // be delivered to all callbacks. main_bps_chid is already a global variable,
        // so this isn't necessary, but is just done here to illustrate the convention.
        err = camera_start_photo_viewfinder(handle,
                                            NULL, //&viewfinder_callback,
                                            &status_callback,
                                            (void*)main_bps_chid);   // user-defined arg.
        if (err != CAMERA_EOK) {
            fprintf(stderr, "camera_start_photo_viewfinder() failed: %d\n", err);
        } else {
            // successfully started viewfinder
            // if it's a front-facing camera, we should mirror the viewfinder once
            // we receive it.
            if (unit == CAMERA_UNIT_FRONT) {
                shouldmirror = true;
            }
            return 0;
        }
    }
    // clean up on error
    camera_close(handle);
    handle = CAMERA_HANDLE_INVALID;
    return err;
}
 void ActionRecordPicture::execute(ExecutionState* state)
 {
     DataModelLogger* RUNLOG = state->getLogger();
     camera_unit_t cameraUnit =
             getParameter("CAMERA", state) == "BACK" ? CAMERA_UNIT_REAR : CAMERA_UNIT_FRONT;
     if (!state->getRuntimeResources()->isCameraOk(
             state->getRuntimeResources()->setupCamera(cameraUnit, CAMERA_MODE_RW))) {
         LOG->error("Failed to get camera");
         RUNLOG->error("Failed to get camera");
         return;
     }
     camera_handle_t camera = state->getRuntimeResources()->getCamera();
     if (!state->getRuntimeResources()->isCameraOk(
             _camera_set_photovf_property(camera, CAMERA_IMGPROP_END))) {
         LOG->error("Failed to set photo view finder properties");
         RUNLOG->error("Failed to set photo view finder properties");
         return;
     }
     if (!state->getRuntimeResources()->isCameraOk(
             _camera_set_photo_property(camera, CAMERA_IMGPROP_END))) {
         LOG->error("Failed to set photo properties");
         RUNLOG->error("Failed to set photo properties");
         return;
     }
     state->getRuntimeResources()->callbackReset();
     if (!state->getRuntimeResources()->isCameraOk(
             camera_start_photo_viewfinder(camera, NULL, viewFinderStatusCallback,
                     (void*) state->getRuntimeResources()))) {
         LOG->error("Failed to start video viewfinder");
         RUNLOG->error("Failed to start video viewfinder");
         return;
     }
     if (!state->getRuntimeResources()->callbackWait(5000)) {
         LOG->warning("Timed out waiting for view finder to start");
     }
     std::string flashModeStr = getParameter("FLASH", state);
     camera_flashmode_t flashMode;
     if (flashModeStr == "ON") {
         flashMode = CAMERA_FLASH_ON;
     } else if (flashModeStr == "OFF") {
         flashMode = CAMERA_FLASH_OFF;
     } else {
         flashMode = CAMERA_FLASH_AUTO;
     }
     if (!state->getRuntimeResources()->isCameraOk(camera_config_flash(camera, flashMode))) {
         LOG->error("Failed to set flash setting");
         RUNLOG->error("Failed to set flash setting");
         return;
     }
     std::string filename;
     if (isValueTrue(getParameter("APPENDTIME", state))) {
         filename = state->getRuntimeResources()->uniqueFilename(getParameter("OUTFILE", state));
     } else {
         filename = getParameter("OUTFILE", state);
     }
     state->setUserProperty("PICTURE_FILENAME", filename);
     state->setExecutionProperty("ACTION_ActionRecordPicture_OUTFILE", filename);
     state->getRuntimeResources()->callbackReset();
     if (!state->getRuntimeResources()->isCameraOk(
             camera_take_photo(camera, NULL, NULL, NULL, photoRecordingStatusCallback,
                     (void*) state, false))) {
         LOG->error("Failed to take photo");
         RUNLOG->error("Failed to take photo");
         return;
     }
     if (!state->getRuntimeResources()->callbackWait(3000)) {
         LOG->warning("Timed out waiting for view finder to start");
         return;
     }
     if (!state->getRuntimeResources()->isCameraOk(camera_stop_photo_viewfinder(camera))) {
         LOG->error("Failed to stop photo viewfinder");
         RUNLOG->error("Failed to stop photo viewfinder");
         return;
     }
     state->getRuntimeResources()->destroyCamera();
     RUNLOG->debug("Photo captured");
 }
// NOTE, in this code I am purposely ignoring error return codes for the sake of clarity while
// walking through the code.  Normally, you should check the return codes for errors, as this
// will greatly help to isolate bugs.
static int
init_camera()
{
    camera_error_t err;
    unsigned int num;
    unsigned int i;
    camera_unit_t cams[CAMERA_UNIT_NUM_UNITS];
    camera_unit_t unit;
    // here are 2 ways to determine which cameras are available on a given device...
#if 1
    // METHOD 1
    // inventory the available camera units
    // NOTE: to just find the number of available cameras: camera_get_cameras(0, &num, NULL);

    camera_get_supported_cameras(CAMERA_UNIT_NUM_UNITS,
                                 &num,
                                 cams);
    for (i=0; i<num; i++) {
        fprintf(stderr, "found camera unit %d\n", cams[i]);
    }
#else
    // METHOD 2
    // inventory cameras which support a given feature set - in this case PHOTO & VIDEO

    camera_feature_t features[] = { CAMERA_FEATURE_PHOTO, CAMERA_FEATURE_VIDEO };
    camera_unit_t next = CAMERA_UNIT_NONE;
    num = 0;
        // note that this is an iterating function call which returns only one "next" unit at a time
	while (camera_find_capable(features,
							   sizeof(features)/sizeof(*features),
							   next,
							   &next) == CAMERA_EOK) {
        cams[num++] = next;
        fprintf(stderr, "camera unit %d supports the required features\n", next);
    }
#endif

    // open the first camera found
    unit = cams[0];
    fprintf(stderr, "selecting camera unit %d\n", unit);
    err = camera_open(unit,
                      CAMERA_MODE_RW | CAMERA_MODE_ROLL,
                      &handle);
    if (err != CAMERA_EOK) {
        fprintf(stderr, "camera_open() failed: %d\n", err);
        return err;
    }

    // This is the minimal required configuration for a viewfinder.
    err = camera_set_photovf_property(handle,
                                      CAMERA_IMGPROP_WIN_GROUPID, vf_group,
                                      CAMERA_IMGPROP_WIN_ID, "my_viewfinder");
#if 0
    // here is a more complex example configuration:
    err = camera_set_photovf_property(handle,
                                      CAMERA_IMGPROP_WIN_GROUPID, vfWndGroupId,
                                      CAMERA_IMGPROP_WIN_ID, vfWndWindowId,
                                      CAMERA_IMGPROP_WIDTH, w,
                                      CAMERA_IMGPROP_HEIGHT, h,
                                      CAMERA_IMGPROP_HWOVERLAY, 1,
#ifdef DO_PHOTO_180
                                      CAMERA_IMGPROP_ROTATION, (rotation+180) % 360,
#else
                                      CAMERA_IMGPROP_ROTATION, rotation,
#endif
#ifdef DO_BURST
                                      CAMERA_IMGPROP_BURSTMODE, 1,
#endif
                                      CAMERA_IMGPROP_FRAMERATE, 15.0);
#endif
    if (err != CAMERA_EOK) {
        // NOTE: if you need to narrow down which setting is causing an error,
        // consider breaking the above command down into multiple calls.
        // be aware that some values must be changed together though (eg. height & width)
        // in order to pass range-checking
        fprintf(stderr, "camera_set_photovf_property() failed: %d\n", err);
    } else {
        
        // a valid photovf and photo configuration are required before starting
        // the viewfinder.
        // the defaults for both will always be sane, however if certain properties
        // are changed in one, they may need to be changed in the other.
        // resolution is one such example (aspect ratios must match).
        // here is an example configuration for photo properties. (just updating rotation)
        camera_set_photo_property(handle,
                                  CAMERA_IMGPROP_ROTATION, 180);

        // callbacks are optional, however status callback is useful for detecting asynchronous events
        // unless your application requires processing of viewfinder frame data, don't bother with
        // a viewfinder callback, as it incurs some additional ipc overhead.  remember, the viewfinder
        // window itself is already rendered by the camera service, not your app.
        err = camera_start_photo_viewfinder(handle,
                                            &viewfinder_callback,
                                            &status_callback,
                                            (void*)123);   // arbitrary user argument
        if (err != CAMERA_EOK) {
            fprintf(stderr, "camera_start_photo_viewfinder() failed: %d\n", err);
        } else {
            // successfully started viewfinder
            // if it's a front-facing camera, we should mirror the viewfinder once
            // we receive it.
            if (unit == CAMERA_UNIT_FRONT) {
                shouldmirror = true;
            }
            return 0;
        }
    }
    // clean up on error
    camera_close(handle);
    handle = CAMERA_HANDLE_INVALID;
    return err;
}