/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
   (Karlsruhe Institute of Technology)

   This library is free software; you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as published by the
   Free Software Foundation; either version 2.1 of the License, or (at your
   option) any later version.

   This library 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 Lesser General Public License for more
   details.

   You should have received a copy of the GNU Lesser General Public License along
   with this library; if not, write to the Free Software Foundation, Inc., 51
   Franklin St, Fifth Floor, Boston, MA 02110, USA */

#ifndef __UNIFIED_CAMERA_ACCESS_H
#define __UNIFIED_CAMERA_ACCESS_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * \file uca.h
 * \brief Abstract camera model
 *
 * The uca_t structure represents a common interface for cameras regardless of
 * their connectivity. Each camera that adheres to this model must provide an
 * initialization function that probes the device and sets all function pointers
 * to their respective implementation.
 */

/**
 * \mainpage
 *
 * \section intro_sec Introduction
 *
 * libuca is a thin wrapper to make different cameras and their access
 * interfaces (via CameraLink, PCIe …) accessible in an easy way.
 * It builds support for cameras, when it can find the necessary dependencies,
 * so there is no need to have camera SDKs installed when you don't own a
 * camera. 
 *
 * \section intro_installation Installation from source
 *
 * Check out the code from vogelgesang/libuca or untar the tarball. Make sure to
 * install CMake and any third party drivers and SDKs that you want to have
 * accessed by libuca. Now go into some empty build directory and issue
 *
 * \verbatim
$ cmake PATH_TO_LIBUCA_SOURCE
$ make
$ make install
   \endverbatim
 *
 * \section intro_quickstart Quick Start
 *
 * First of all you have to create a new uca handle to initialize the individual
 * subsystems
 *
 * \code uca *u = uca_init(); \endcode
 *
 * and see if uca_init() did not return NULL. If this is the case, no camera or
 * frame grabber was found. If you build with HAVE_DUMMY_CAMERA, there will
 * always be at least the dummy camera available.
 *
 * You can then iterate through all available cameras using
 * 
 * \code
 * uca_camera *i = uca->cameras;
 * while (i != NULL) {
 *     // do something with i
 *     i = i->next;
 * }
 * \endcode
 *
 * The uca_camera handle is used to set and retrieve properties: 
 *
 * \code
 * uca_camera *cam = uca->cameras;
 * uint32_t val = 5000;
 * uca_cam_set_property(cam, UCA_PROP_EXPOSURE, &val);
 * 
 * uint32_t width, height;
 * uca_cam_get_property(cam, UCA_PROP_WIDTH, &width, NULL);
 * uca_cam_get_property(cam, UCA_PROP_HEIGHT, &height, NULL);
 * \endcode
 *
 * \section properties Property system
 *
 * Each property has a unique id listed in the #uca_property_ids enum.
 * Information about each property is given in a uca_property structure that is
 * mapped with uca_get_full_property(). The description lists the unit, type and
 * access rights.
 * 
 * \section intro_recording Recording
 *
 * To record in synchronous fashion use uca_cam_grab() like
 *
 * \code
 * void *buffer = (void *) malloc(width * height * bytes_per_pixel);
 * uca_cam_start_recording(cam);
 * uca_cam_grab(cam, (char *) buffer, NULL);
 * uca_cam_stop_recording(cam);
 * \endcode
 *
 * Eventually you will have to cleanup the system by calling 
 *
 * \code uca_destroy(u); \endcode
 *
 * \section intro_usage Adding new cameras
 *
 * To add a new camera, add cameras/new-cam.c and cameras/new-cam.h to the
 * source tree and change CMakeLists.txt to include these files.
 * Furthermore, if this camera relies on external dependencies, these have
 * to be found first via the CMake system.
 *
 * The new camera must export exactly one function: uca_new_camera_init() which
 * checks if (given the grabber) the camera is available and sets the function
 * pointers to access the camera accordingly.
 *
 * \section api_reference API reference
 *
 * All function definitions can be found in uca.h.
 *
 */

/* The property IDs must start with 0 and must be continuous. Whenever this
 * library is released, the IDs must not change to guarantee binary compatibility! */
/**
 * ID of all supported properties
 */
typedef enum {
    UCA_PROP_NAME = 0,
    UCA_PROP_WIDTH,
    UCA_PROP_WIDTH_MIN,
    UCA_PROP_WIDTH_MAX,
    UCA_PROP_HEIGHT,
    UCA_PROP_HEIGHT_MIN,
    UCA_PROP_HEIGHT_MAX,
    UCA_PROP_X_OFFSET,
    UCA_PROP_X_OFFSET_MIN,
    UCA_PROP_X_OFFSET_MAX,
    UCA_PROP_Y_OFFSET,
    UCA_PROP_Y_OFFSET_MIN,
    UCA_PROP_Y_OFFSET_MAX,
    UCA_PROP_BINNING_X,
    UCA_PROP_BINNING_Y,
    UCA_PROP_BITDEPTH,
    UCA_PROP_EXPOSURE,
    UCA_PROP_EXPOSURE_MIN,
    UCA_PROP_EXPOSURE_MAX,
    UCA_PROP_DELAY,
    UCA_PROP_DELAY_MIN,
    UCA_PROP_DELAY_MAX,
    UCA_PROP_FRAMERATE,
    UCA_PROP_TEMPERATURE_SENSOR,
    UCA_PROP_TEMPERATURE_CAMERA,
    UCA_PROP_TRIGGER_MODE,
    UCA_PROP_TRIGGER_EXPOSURE,

    UCA_PROP_PGA_GAIN,
    UCA_PROP_PGA_GAIN_MIN,
    UCA_PROP_PGA_GAIN_MAX,
    UCA_PROP_PGA_GAIN_STEPS,
    UCA_PROP_ADC_GAIN,
    UCA_PROP_ADC_GAIN_MIN,
    UCA_PROP_ADC_GAIN_MAX,
    UCA_PROP_ADC_GAIN_STEPS,

    /* grabber specific */
    UCA_PROP_GRAB_TIMEOUT,
    UCA_PROP_GRAB_SYNCHRONOUS,
    UCA_PROP_GRAB_AUTO,

    /* pco.edge specific */
    UCA_PROP_TIMESTAMP_MODE,
    UCA_PROP_SCAN_MODE,
    UCA_PROP_HOTPIXEL_CORRECTION,

    /* IPE camera specific */
    UCA_PROP_INTERLACE_SAMPLE_RATE,
    UCA_PROP_INTERLACE_PIXEL_THRESH,
    UCA_PROP_INTERLACE_ROW_THRESH,

    /* Photon Focus specific */
    UCA_PROP_CORRECTION_MODE,

    UCA_PROP_LAST
} uca_property_ids;

/* Possible timestamp modes for UCA_PROP_TIMESTAMP_MODE */
#define UCA_TIMESTAMP_NONE      0x00
#define UCA_TIMESTAMP_ASCII     0x01
#define UCA_TIMESTAMP_BINARY    0x02

/* Trigger mode for UCA_PROP_TRIGGER_MODE */
#define UCA_TRIGGER_AUTO        0   /**< free-run mode */
#define UCA_TRIGGER_SOFTWARE    1   /**< software trigger via uca_cam_trigger() */
#define UCA_TRIGGER_EXTERNAL    2   /**< external hardware trigger */
#define UCA_TRIGGER_EXTERNAL_EXPOSURE 3     /**< hardware trigger controlling exposure */

#define UCA_TRIGGER_EXP_CAMERA  1   /**< camera-controlled exposure time */
#define UCA_TRIGGER_EXP_LEVEL   2   /**< level-controlled (trigger signal) exposure time */

/* Correction modes for UCA_PROP_CORRECTION_MODE */
#define UCA_CORRECT_OFFSET      0x01
#define UCA_CORRECT_HOTPIXEL    0x02
#define UCA_CORRECT_GAIN        0x04

/**
 * The physical unit of this property.
 *
 * This is important in order to let the camera drivers know, how to convert
 * the values into their respective target value. It is also used for human
 * interfaces.
 */
typedef enum {
    uca_pixel,  /**< number of pixels */
    uca_bits,   /**< number of bits */
    uca_ns,     /**< nanoseconds */
    uca_us,     /**< microseconds */
    uca_ms,     /**< milliseconds */
    uca_s,      /**< seconds */
    uca_rows,   /**< number of rows */
    uca_fps,    /**< frames per second */
    uca_dc,     /**< degree celsius */
    uca_bool,   /**< 1 or 0 for true and false */
    uca_na      /**< no unit available (for example modes) */
} uca_unit;

/**
 * The data type of this property.
 *
 * When using uca_cam_set_property() and uca_cam_get_property() this field
 * must be respected and correct data transfered, as the values are
 * interpreted like defined here.
 */
typedef enum {
    uca_uint32t,
    uca_uint8t,
    uca_string
} uca_types;

/**
 * Access rights determine if uca_cam_get_property() and/or
 * uca_cam_set_property() can be used with this property.
 */
typedef enum {
    uca_read = 0x01,                /**< property can be read */
    uca_write = 0x02,               /**< property can be written */
    uca_readwrite = 0x01 | 0x02     /**< property can be read and written */
} uca_access_rights;

/**
 * Describes the current state of the camera.
 *
 * \see uca_cam_get_state()
 */
typedef enum {
    UCA_CAM_CONFIGURABLE,   /**< Camera can be configured and is not recording */
    UCA_CAM_ARMED,          /**< Camera is ready for recording */
    UCA_CAM_RECORDING,      /**< Camera is currently recording */
    UCA_CAM_READOUT         /**< Camera recorded and is currently in readout mode */
} uca_cam_state;

/**
 * Specify if the callback function keeps the buffer and will call
 * ufo_cam_release_buffer() at later time or if after returning the buffer can
 * be released automatically.
 *
 * \since 0.5
 */
typedef enum {
    UCA_BUFFER_KEEP,        /**< Keep the buffer and call ufo_cam_release_buffer() manually */
    UCA_BUFFER_RELEASE      /**< Buffer is released upon return */
} uca_buffer_status;

/**
 * A uca_property_t describes a vendor-independent property used by cameras and
 * frame grabbers. It basically consists of a human-readable name, a physical
 * unit, a type and some access rights.
 * \see uca_get_full_property()
 */
typedef struct {
    /**
     * A human-readable string for this property.
     *
     * A name is defined in a tree-like structure, to build some form of
     * hierarchical namespace. To define a parent-child-relationship a dot '.'
     * is used. For example "image.width.min" might be the name for the minimal
     * acceptable frame width.
     */
    const char *name;

    uca_unit unit;  /**< Physical unit of this property */
    uca_types type; /**< Type of this property */
    uca_access_rights access; /**< Access rights of this property */

} uca_property;

union uca_value {
    uint32_t u32;
    uint8_t u8;
    char *string;
};


/**
 * Grab callback.
 * 
 * Register such a callback function with uca_cam_register_callback() to
 * receive data as soon as it is delivered.
 *
 * \param[in] image_number Current frame number
 * \param[in] buffer Image data.
 * \param[in] meta_data Meta data provided by the camera specifying per-frame
 *   data.
 * \param[in] user User data registered in uca_cam_register_callback()
 * \return Value from uca_buffer_status. If #UCA_BUFFER_KEEP is returned, the
 * callee must make sure to call uca_cam_release_buffer(). On the other hand, if
 * #UCA_BUFFER_RELEASE is returned this is done by the caller.
 *
 * \note The meta data parameter is not yet specified but just a place holder.
 */
typedef uca_buffer_status (*uca_cam_grab_callback) (uint64_t image_number, void *buffer, void *meta_data, void *user);

extern const char *uca_unit_map[];      /**< maps unit numbers to corresponding strings */


/**
 * An error code is a 32 bit integer with the following format (x:y means x bits
 * for purpose y):
 *
 *    [ 31 (MSB) ...                     ... 0 (LSB) ]
 *    [ 4:lvl | 4:rsv | 4:class | 4:source | 16:code ]
 *
 * where
 *
 *  - lvl describes severity such as warning or failure,
 *  - rsv is reserved,
 *  - class describes the general class of the error,
 *  - source describes where the error occured and
 *  - code is the actual error condition
 *
 * UCA_ERR_MASK_*s can be used to mask the desired field of the error code.
 * 
 */

#define UCA_NO_ERROR            0x00000000

#define UCA_ERR_MASK_CODE       0xF000FFFF
#define UCA_ERR_MASK_SOURCE     0x000F0000
#define UCA_ERR_MASK_TYPE       0x00F00000
#define UCA_ERR_MASK_RESRV      0x0F000000
#define UCA_ERR_MASK_LEVEL      0xF0000000

#define UCA_ERR_GRABBER         0x00010000
#define UCA_ERR_CAMERA          0x00020000

#define UCA_ERR_INIT            0x00100000  /**< error during initialization */
#define UCA_ERR_PROP            0x00200000  /**< error while setting/getting property */
#define UCA_ERR_CALLBACK        0x00300000  /**< callback-related errors */
#define UCA_ERR_TRIGGER         0x00400000  /**< errors concerning trigger */
#define UCA_ERR_CONFIGURATION   0x00500000  /**< errors related to configuration steps */

#define UCA_ERR_FAILURE         0x10000000
#define UCA_ERR_WARNING         0x20000000

#define UCA_ERR_UNCLASSIFIED    0x10000001
#define UCA_ERR_NOT_FOUND       0x10000002
#define UCA_ERR_INVALID         0x10000003
#define UCA_ERR_NO_MEMORY       0x10000004
#define UCA_ERR_OUT_OF_RANGE    0x10000005
#define UCA_ERR_ACQUIRE         0x10000006
#define UCA_ERR_IS_RECORDING    0x10000007 /**< error because device is recording */
#define UCA_ERR_NOT_RECORDING   0x10000008
#define UCA_ERR_FRAME_TRANSFER  0x10000009
#define UCA_ERR_ALREADY_REGISTERED 0x1000000A
#define UCA_ERR_NOT_IMPLEMENTED 0x1000000B
#define UCA_ERR_NO_MORE_IMAGES  0x1000000C

struct uca_camera_priv;
/**
 * uca_camera is an opaque structure that is only accessed with the uca_cam_*
 * methods.
 */
typedef struct uca_camera {
    struct uca_camera *next;
    struct uca_camera_priv* priv;
} uca_camera;

struct uca_grabber_priv;
typedef struct uca_grabber {
    struct uca_grabber *next;
    struct uca_grabber_priv* priv;
} uca_grabber;

/**
 * Keeps a list of cameras and grabbers.
 */
typedef struct {
    uca_camera *cameras;    /**< Root of detected camera list */
    uca_grabber *grabbers;  /**< Root of detected grabber list */
} uca;

/**
 * Initialize the unified camera access interface.
 *
 * \param[in] config_filename Configuration file in JSON format for cameras
 *   relying on external calibration data. It is ignored when no JSON parser can
 *   be found at compile time or config_filename is NULL.
 *
 * \return Pointer to a #uca structure
 *
 * \note uca_init() is thread-safe if a Pthread-implementation is available.
 */
uca *uca_init(const char *config_filename);

/**
 * Free resources of the unified camera access interface
 *
 * \note uca_destroy() is thread-safe if a Pthread-implementation is available.
 */
void uca_destroy(uca *u);

/**
 * Convert a property string to the corresponding ID
 *
 * \param[in] property_name Name of the property
 * \param[out] prop_id Resulting property ID
 * \return Error code
 */
uint32_t uca_get_property_id(const char *property_name, uca_property_ids *prop_id);

/**
 * Convert a property ID to the corresponding string 
 *
 * \param property_id ID of a property 
 * \return If property is found name of the property else NULL
 */
const char* uca_get_property_name(uca_property_ids property_id);

/**
 * Return the full property structure for a given ID
 *
 * \param property_id ID of a property
 * \return Property description or NULL if property is not found
 */
uca_property *uca_get_full_property(uca_property_ids property_id);

/**
 * Allocates buffer memory for the internal frame grabber.
 *
 * The allocation is just a hint to the underlying camera driver. It might
 * ignore this or pass this information on to a related frame grabber.
 *
 * \param[in] cam A uca_camera object
 * \param[in] n_buffers Number of sub-buffers with size frame_width*frame_height.
 * \return Error code
 */
uint32_t uca_cam_alloc(uca_camera *cam, uint32_t n_buffers);

/**
 * Retrieve current state of the camera.
 *
 * \param[in] cam A uca_camera object
 * \return A value from the uca_cam_state enum representing the current state of
 *   the camera.
 */
uca_cam_state uca_cam_get_state(uca_camera *cam);


/**
 * Set a camera property.
 *
 * \param[in] cam The camera whose properties are to be set.
 * \param[in] cam A uca_camera object
 * \param[in] property ID of the property as defined in XXX
 * \param[out] data Where to read the property's value from
 *
 * \return UCA_ERR_PROP_INVALID if property is not supported on the camera or
 *   UCA_ERR_PROP_VALUE_OUT_OF_RANGE if value cannot be set.
 */
uint32_t uca_cam_set_property(uca_camera *cam, uca_property_ids property, void *data);

/**
 * Get a property.
 *
 * \param[in] cam A uca_camera object
 * \param[in] property ID of the property as defined in XXX
 * \param[out] data Where to store the property's value
 * \param[in] num Number of bytes of string storage. Ignored for uca_uint8t
 *   and uca_uint32t properties.
 *
 * \return UCA_ERR_PROP_INVALID if property is not supported on the camera
 */
uint32_t uca_cam_get_property(uca_camera *cam, uca_property_ids property, void *data, size_t num);

/**
 * Begin recording.
 *
 * Usually this also involves the frame acquisition of the frame grabber but is
 * hidden by this function.
 *
 * \param[in] cam A uca_camera object
 * \return Error code
 */
uint32_t uca_cam_start_recording(uca_camera *cam);

/**
 * Stop recording.
 *
 * \param[in] cam A uca_camera object
 * \return Error code
 */
uint32_t uca_cam_stop_recording(uca_camera *cam);

/**
 * Send a software trigger signal to start a sensor read-out.
 *
 * This method is only useful when #UCA_PROP_TRIGGER_MODE is set to
 * #UCA_TRIGGER_SOFTWARE.
 *
 * \param[in] cam A uca_camera object
 * \return Error code
 */
uint32_t uca_cam_trigger(uca_camera *cam);

/**
 * Register callback for given frame grabber. To actually start receiving
 * frames, call uca_grabber_acquire().
 *
 * \param[in] cam A uca_camera object
 * \param[in] callback Callback function for when a frame arrived
 * \param[in] user User data that is passed to the callback function
 * \return Error code
 */
uint32_t uca_cam_register_callback(uca_camera *cam, uca_cam_grab_callback callback, void *user);

/**
 * Release the buffer that was given in the grab callback.
 *
 * \param[in] cam A uca_camera object
 * \param[in] buffer The buffer that was passed to the callback
 *
 * \see uca_cam_register_callback(), uca_cam_grab_callback
 * \since 0.5
 */
uint32_t uca_cam_release_buffer(uca_camera *cam, void *buffer);

/**
 * \brief Grab one image from the camera
 * 
 * The grabbing involves a memory copy because we might have to decode the image
 * coming from the camera, which the frame grabber is not able to do.
 *
 * \param[in] cam A uca_camera object
 * \param[in] buffer Destination buffer
 * \param[in] meta_data Meta data provided by the camera specifying per-frame
 *   data.
 * \return Error code
 *
 * \note The meta data parameter is not yet specified but just a place holder.
 *
 */
uint32_t uca_cam_grab(uca_camera *cam, char *buffer, void *meta_data);

/**
 * \brief Initiate read out for recording based cameras like pco.dimax or
 * Photron SAx
 *
 * This function merely starts read out and requires that recording has stopped
 * with uca_cam_stop_recording. To retrieve the image data, you have still have
 * to use uca_cam_grab.
 *
 * \param[in] cam A uca_camera object
 * \return Error code
 */
uint32_t uca_cam_readout(uca_camera *cam);

#define uca_set_void(p, type, value) { *((type *) p) = (type) value; }

#ifdef __cplusplus
}
#endif

#endif