/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Contains emulated camera service implementation.
 */

#include "qemu-common.h"
#include "android/globals.h"  /* for android_hw */
#include "android/hw-qemud.h"
#include "android/utils/misc.h"
#include "android/utils/system.h"
#include "android/utils/debug.h"
#include "android/camera/camera-capture.h"
#include "android/camera/camera-format-converters.h"
#include "android/camera/camera-service.h"

#define  E(...)    derror(__VA_ARGS__)
#define  W(...)    dwarning(__VA_ARGS__)
#define  D(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
#define  D_ACTIVE  VERBOSE_CHECK(camera)

/* the T(...) macro is used to dump traffic */
#define  T_ACTIVE   0

#if T_ACTIVE
#define  T(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
#else
#define  T(...)    ((void)0)
#endif

/* Defines name of the camera service. */
#define SERVICE_NAME    "camera"

/* Maximum number of supported emulated cameras. */
#define MAX_CAMERA      8

/* Camera sevice descriptor. */
typedef struct CameraServiceDesc CameraServiceDesc;
struct CameraServiceDesc {
    /* Information about camera devices connected to the host.
     * Note that once initialized, entries in this array are considered to be
     * constant. */
    CameraInfo  camera_info[MAX_CAMERA];
    /* Number of camera devices connected to the host. */
    int         camera_count;
};

/* One and only one camera service. */
static CameraServiceDesc    _camera_service_desc;

/********************************************************************************
 * Helper routines
 *******************************************************************************/

/* Extracts query name, and (optionally) query parameters from the query string.
 * Param:
 *  query - Query string. Query string in the camera service are formatted as such:
 *          "<query name>[ <parameters>]",
 *      where parameters are optional, and if present, must be separated from the
 *      query name with a single ' '. See comments to get_token_value routine
 *      for the format of the parameters string.
 *  query_name - Upon success contains query name extracted from the query
 *      string.
 *  query_name_size - Buffer size for 'query_name' string.
 *  query_param - Upon success contains a pointer to the beginning of the query
 *      parameters. If query has no parameters, NULL will be passed back with
 *      this parameter. This parameter is optional and can be NULL.
 * Return:
 *  0 on success, or number of bytes required for query name if 'query_name'
 *  string buffer was too small to contain it.
 */
static int
_parse_query(const char* query,
             char* query_name,
             int query_name_size,
             const char** query_param)
{
    /* Extract query name. */
    const char* qend = strchr(query, ' ');
    if (qend == NULL) {
        qend = query + strlen(query);
    }
    if ((qend - query) >= query_name_size) {
        return qend - query + 1;
    }
    memcpy(query_name, query, qend - query);
    query_name[qend - query] = '\0';

    /* Calculate query parameters pointer (if needed) */
    if (query_param != NULL) {
        if (*qend == ' ') {
            qend++;
        }
        *query_param = (*qend == '\0') ? NULL : qend;
    }

    return 0;
}

/* Appends one string to another, growing the destination string buffer if
 * needed.
 * Param:
 *  str_buffer - Contains pointer to the destination string buffer. Content of
 *      this parameter can be NULL. Note that content of this parameter will
 *      change if string buffer has been reallocated.
 *  str_buf_size - Contains current buffer size of the string, addressed by
 *      'str_buffer' parameter. Note that content of this parameter will change
 *      if string buffer has been reallocated.
 *  str - String to append.
 * Return:
 *  0 on success, or -1 on failure (memory allocation).
 */
static int
_append_string(char** str_buf, size_t* str_buf_size, const char* str)
{
    const size_t offset = (*str_buf != NULL) ? strlen(*str_buf) : 0;
    const size_t append_bytes = strlen(str) + 1;

    /* Make sure these two match. */
    if (*str_buf == NULL) {
        *str_buf_size = 0;
    }

    if ((offset + append_bytes) > *str_buf_size) {
        /* Reallocate string, so it can fit what's being append to it. Note that
         * we reallocate a bit bigger buffer than is needed in order to minimize
         * number of memory allocation calls in case there are more "appends"
         * coming. */
        const size_t required_mem = offset + append_bytes + 256;
        char* new_buf = (char*)realloc(*str_buf, required_mem);
        if (new_buf == NULL) {
            E("%s: Unable to allocate %d bytes for a string",
              __FUNCTION__, required_mem);
            return -1;
        }
        *str_buf = new_buf;
        *str_buf_size = required_mem;
    }
    memcpy(*str_buf + offset, str, append_bytes);

    return 0;
}

/* Represents camera information as a string formatted as follows:
 *  'name=<devname> channel=<num> pix=<format> facing=<direction> framedims=<widh1xheight1,...>\n'
 * Param:
 *  ci - Camera information descriptor to convert into a string.
 *  str - Pointer to the string buffer where to save the converted camera
 *      information descriptor. On entry, content of this parameter can be NULL.
 *      Note that string buffer addressed with this parameter may be reallocated
 *      in this routine, so (if not NULL) it must contain a buffer allocated with
 *      malloc.  The caller is responsible for freeing string buffer returned in
 *      this parameter.
 *  str_size - Contains byte size of the buffer addressed by 'str' parameter.
 * Return:
 *  0 on success, or != 0 on failure.
 */
static int
_camera_info_to_string(const CameraInfo* ci, char** str, size_t* str_size) {
    int res;
    int n;
    char tmp[128];

    /* Append device name. */
    snprintf(tmp, sizeof(tmp), "name=%s ", ci->device_name);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append input channel. */
    snprintf(tmp, sizeof(tmp), "channel=%d ", ci->inp_channel);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append pixel format. */
    snprintf(tmp, sizeof(tmp), "pix=%d ", ci->pixel_format);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append direction. */
    snprintf(tmp, sizeof(tmp), "dir=%s ", ci->direction);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append supported frame sizes. */
    snprintf(tmp, sizeof(tmp), "framedims=%dx%d",
             ci->frame_sizes[0].width, ci->frame_sizes[0].height);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    for (n = 1; n < ci->frame_sizes_num; n++) {
        snprintf(tmp, sizeof(tmp), ",%dx%d",
                 ci->frame_sizes[n].width, ci->frame_sizes[n].height);
        res = _append_string(str, str_size, tmp);
        if (res) {
            return res;
        }
    }

    /* Stringified camera properties should end with EOL. */
    return _append_string(str, str_size, "\n");
}

/* Gets camera information matching a display name.
 * Param:
 *  disp_name - Display name to match.
 *  arr - Array of camera informations.
 *  num - Number of elements in the array.
 * Return:
 *  Matching camera information, or NULL if matching camera information for the
 *  given display name has not been found in the array.
 */
static CameraInfo*
_camera_info_get_by_display_name(const char* disp_name, CameraInfo* arr, int num)
{
    int n;
    for (n = 0; n < num; n++) {
        if (!arr[n].in_use && arr[n].display_name != NULL &&
            !strcmp(arr[n].display_name, disp_name)) {
            return &arr[n];
        }
    }
    return NULL;
}

/* Gets camera information matching a device name.
 * Param:
 *  device_name - Device name to match.
 *  arr - Array of camera informations.
 *  num - Number of elements in the array.
 * Return:
 *  Matching camera information, or NULL if matching camera information for the
 *  given device name has not been found in the array.
 */
static CameraInfo*
_camera_info_get_by_device_name(const char* device_name, CameraInfo* arr, int num)
{
    int n;
    for (n = 0; n < num; n++) {
        if (arr[n].device_name != NULL && !strcmp(arr[n].device_name, device_name)) {
            return &arr[n];
        }
    }
    return NULL;
}

/********************************************************************************
 * CameraServiceDesc API
 *******************************************************************************/

/* Initialized webcam emulation record in camera service descriptor.
 * Param:
 *  csd - Camera service descriptor to initialize a record in.
 *  disp_name - Display name of a web camera ('webcam<N>') to use for emulation.
 *  dir - Direction ('back', or 'front') that emulated camera is facing.
 *  ci, ci_cnt - Array of webcam information for enumerated web cameras connected
 *      to the host.
 */
static void
_wecam_setup(CameraServiceDesc* csd,
             const char* disp_name,
             const char* dir,
             CameraInfo* ci,
             int ci_cnt)
{
    /* Find webcam record in the list of enumerated web cameras. */
    CameraInfo* found = _camera_info_get_by_display_name(disp_name, ci, ci_cnt);
    if (found == NULL) {
        W("Camera name '%s' is not found in the list of connected cameras.\n"
          "Use '-webcam-list' emulator option to obtain the list of connected camera names.\n",
          disp_name);
        return;
    }

    /* Save to the camera info array that will be used by the service. */
    memcpy(csd->camera_info + csd->camera_count, found, sizeof(CameraInfo));
    /* This camera is taken. */
    found->in_use = 1;
    /* Update direction parameter. */
    if (csd->camera_info[csd->camera_count].direction != NULL) {
        free(csd->camera_info[csd->camera_count].direction);
    }
    csd->camera_info[csd->camera_count].direction = ASTRDUP(dir);
    D("Camera %d '%s' connected to '%s' facing %s using %.4s pixel format",
      csd->camera_count, csd->camera_info[csd->camera_count].display_name,
      csd->camera_info[csd->camera_count].device_name,
      csd->camera_info[csd->camera_count].direction,
      (const char*)(&csd->camera_info[csd->camera_count].pixel_format));
      csd->camera_count++;
}

/* Initializes camera service descriptor.
 */
static void
_camera_service_init(CameraServiceDesc* csd)
{
    CameraInfo ci[MAX_CAMERA];
    int connected_cnt;

    /* Enumerate camera devices connected to the host. */
    memset(ci, 0, sizeof(CameraInfo) * MAX_CAMERA);
    memset(csd->camera_info, 0, sizeof(CameraInfo) * MAX_CAMERA);
    csd->camera_count = 0;

    /* Lets see if HW config uses web cameras. */
    if (memcmp(android_hw->hw_camera_back, "webcam", 6) &&
        memcmp(android_hw->hw_camera_front, "webcam", 6)) {
        /* Web camera emulation is disabled. Skip enumeration of webcameras. */
        return;
    }

    /* Enumerate web cameras connected to the host. */
    connected_cnt = enumerate_camera_devices(ci, MAX_CAMERA);
    if (connected_cnt <= 0) {
        /* Nothing is connected - nothing to emulate. */
        return;
    }

    /* Set up back camera emulation. */
    if (!memcmp(android_hw->hw_camera_back, "webcam", 6)) {
        _wecam_setup(csd, android_hw->hw_camera_back, "back", ci, connected_cnt);
    }

    /* Set up front camera emulation. */
    if (!memcmp(android_hw->hw_camera_front, "webcam", 6)) {
        _wecam_setup(csd, android_hw->hw_camera_front, "front", ci, connected_cnt);
    }
}

/* Gets camera information for the given camera device name.
 * Param:
 *  cs - Initialized camera service descriptor.
 *  device_name - Camera's device name to look up the information for.
 * Return:
 *  Camera information pointer on success, or NULL if no camera information has
 *  been found for the given device name.
 */
static CameraInfo*
_camera_service_get_camera_info_by_device_name(CameraServiceDesc* cs,
                                               const char* device_name)
{
    return _camera_info_get_by_device_name(device_name, cs->camera_info,
                                           cs->camera_count);
}

/********************************************************************************
 * Helpers for handling camera client queries
 *******************************************************************************/

/* Formats paload size according to the protocol, and sends it to the client.
 * To simplify endianess handling we convert payload size to an eight characters
 * string, representing payload size value in hexadecimal format.
 * Param:
 *  qc - Qemu client to send the payload size to.
 *  payload_size - Payload size to report to the client.
 */
static void
_qemu_client_reply_payload(QemudClient* qc, size_t payload_size)
{
    char payload_size_str[9];
    snprintf(payload_size_str, sizeof(payload_size_str), "%08zx", payload_size);
    qemud_client_send(qc, (const uint8_t*)payload_size_str, 8);
}

/*
 * Prefixes for replies to camera client queries.
 */

/* Success, no data to send in reply. */
#define OK_REPLY        "ok"
/* Failure, no data to send in reply. */
#define KO_REPLY        "ko"
/* Success, there are data to send in reply. */
#define OK_REPLY_DATA   OK_REPLY ":"
/* Failure, there are data to send in reply. */
#define KO_REPLY_DATA   KO_REPLY ":"

/* Builds and sends a reply to a query.
 * All replies to a query in camera service have a prefix indicating whether the
 * query has succeeded ("ok"), or failed ("ko"). The prefix can be followed by
 * extra data, containing response to the query. In case there are extra data,
 * they are separated from the prefix with a ':' character.
 * Param:
 *  qc - Qemu client to send the reply to.
 *  ok_ko - An "ok", or "ko" selector, where 0 is for "ko", and !0 is for "ok".
 *  extra - Optional extra query data. Can be NULL.
 *  extra_size - Extra data size.
 */
static void
_qemu_client_query_reply(QemudClient* qc,
                         int ok_ko,
                         const void* extra,
                         size_t extra_size)
{
    const char* ok_ko_str;
    size_t payload_size;

    /* Make sure extra_size is 0 if extra is NULL. */
    if (extra == NULL && extra_size != 0) {
        W("%s: 'extra' = NULL, while 'extra_size' = %d",
          __FUNCTION__, (int)extra_size);
        extra_size = 0;
    }

    /* Calculate total payload size, and select appropriate 'ok'/'ko' prefix */
    if (extra_size) {
        /* 'extra' size + 2 'ok'/'ko' bytes + 1 ':' separator byte. */
        payload_size = extra_size + 3;
        ok_ko_str = ok_ko ? OK_REPLY_DATA : KO_REPLY_DATA;
    } else {
        /* No extra data: just zero-terminated 'ok'/'ko'. */
        payload_size = 3;
        ok_ko_str = ok_ko ? OK_REPLY : KO_REPLY;
    }

    /* Send payload size first. */
    _qemu_client_reply_payload(qc, payload_size);
    /* Send 'ok[:]'/'ko[:]' next. Note that if there is no extra data, we still
     * need to send a zero-terminator for 'ok'/'ko' string instead of the ':'
     * separator. So, one way or another, the prefix is always 3 bytes. */
    qemud_client_send(qc, (const uint8_t*)ok_ko_str, 3);
    /* Send extra data (if present). */
    if (extra != NULL) {
        qemud_client_send(qc, (const uint8_t*)extra, extra_size);
    }
}

/* Replies query success ("OK") back to the client.
 * Param:
 *  qc - Qemu client to send the reply to.
 *  ok_str - An optional string containing query results. Can be NULL.
 */
static void
_qemu_client_reply_ok(QemudClient* qc, const char* ok_str)
{
    _qemu_client_query_reply(qc, 1, ok_str,
                             (ok_str != NULL) ? (strlen(ok_str) + 1) : 0);
}

/* Replies query failure ("KO") back to the client.
 * Param:
 *  qc - Qemu client to send the reply to.
 *  ko_str - An optional string containing reason for failure. Can be NULL.
 */
static void
_qemu_client_reply_ko(QemudClient* qc, const char* ko_str)
{
    _qemu_client_query_reply(qc, 0, ko_str,
                             (ko_str != NULL) ? (strlen(ko_str) + 1) : 0);
}

/********************************************************************************
 * Camera Factory API
 *******************************************************************************/

/* Handles 'list' query received from the Factory client.
 * Response to this query is a string that represents each connected camera in
 * this format: 'name=devname framedims=widh1xheight1,widh2xheight2,widhNxheightN\n'
 * Strings, representing each camera are separated with EOL symbol.
 * Param:
 *  csd, client - Factory serivice, and client.
 * Return:
 *  0 on success, or != 0 on failure.
 */
static int
_factory_client_list_cameras(CameraServiceDesc* csd, QemudClient* client)
{
    int n;
    size_t reply_size = 0;
    char* reply = NULL;

    /* Lets see if there was anything found... */
    if (csd->camera_count == 0) {
        /* No cameras connected to the host. Reply with "\n" */
        _qemu_client_reply_ok(client, "\n");
        return 0;
    }

    /* "Stringify" each camera information into the reply string. */
    for (n = 0; n < csd->camera_count; n++) {
        const int res =
            _camera_info_to_string(csd->camera_info + n, &reply, &reply_size);
        if (res) {
            if (reply != NULL) {
                free(reply);
            }
            _qemu_client_reply_ko(client, "Memory allocation error");
            return res;
        }
    }

    D("%s Replied: %s", __FUNCTION__, reply);
    _qemu_client_reply_ok(client, reply);
    free(reply);

    return 0;
}

/* Handles a message received from the emulated camera factory client.
 * Queries received here are represented as strings:
 *  'list' - Queries list of cameras connected to the host.
 * Param:
 *  opaque - Camera service descriptor.
 *  msg, msglen - Message received from the camera factory client.
 *  client - Camera factory client pipe.
 */
static void
_factory_client_recv(void*         opaque,
                     uint8_t*      msg,
                     int           msglen,
                     QemudClient*  client)
{
    /*
     * Emulated camera factory client queries.
     */

    /* List cameras connected to the host. */
    static const char _query_list[]     = "list";

    CameraServiceDesc* csd = (CameraServiceDesc*)opaque;
    char query_name[64];
    const char* query_param = NULL;

    /* Parse the query, extracting query name and parameters. */
    if (_parse_query((const char*)msg, query_name, sizeof(query_name),
                     &query_param)) {
        E("%s: Invalid format in query '%s'", __FUNCTION__, (const char*)msg);
        _qemu_client_reply_ko(client, "Invalid query format");
        return;
    }

    D("%s Camera factory query '%s'", __FUNCTION__, query_name);

    /* Dispatch the query to an appropriate handler. */
    if (!strcmp(query_name, _query_list)) {
        /* This is a "list" query. */
        _factory_client_list_cameras(csd, client);
    } else {
        E("%s: Unknown camera factory query name in '%s'",
          __FUNCTION__, (const char*)msg);
        _qemu_client_reply_ko(client, "Unknown query name");
    }
}

/* Emulated camera factory client has been disconnected from the service. */
static void
_factory_client_close(void*  opaque)
{
    /* There is nothing to clean up here: factory service is just an alias for
     * the "root" camera service, that doesn't require anything more, than camera
     * dervice descriptor already provides. */
}

/********************************************************************************
 * Camera client API
 *******************************************************************************/

/* Describes an emulated camera client.
 */
typedef struct CameraClient CameraClient;
struct CameraClient
{
    /* Client name.
     *  On Linux this is the name of the camera device.
     *  On Windows this is the name of capturing window.
     */
    char*               device_name;
    /* Input channel to use to connect to the camera. */
    int                 inp_channel;
    /* Camera information. */
    const CameraInfo*   camera_info;
    /* Emulated camera device descriptor. */
    CameraDevice*       camera;
    /* Buffer allocated for video frames.
     * Note that memory allocated for this buffer
     * also contains preview framebuffer. */
    uint8_t*            video_frame;
    /* Preview frame buffer.
     * This address points inside the 'video_frame' buffer. */
    uint16_t*           preview_frame;
    /* Byte size of the videoframe buffer. */
    size_t              video_frame_size;
    /* Byte size of the preview frame buffer. */
    size_t              preview_frame_size;
    /* Pixel format required by the guest. */
    uint32_t            pixel_format;
    /* Frame width. */
    int                 width;
    /* Frame height. */
    int                 height;
    /* Number of pixels in a frame buffer. */
    int                 pixel_num;
    /* Status of video and preview frame cache. */
    int                 frames_cached;
};

/* Frees emulated camera client descriptor. */
static void
_camera_client_free(CameraClient* cc)
{
    /* The only exception to the "read only" rule: we have to mark the camera
     * as being not used when we destroy a service for it. */
    if (cc->camera_info != NULL) {
        ((CameraInfo*)cc->camera_info)->in_use = 0;
    }
    if (cc->camera != NULL) {
        camera_device_close(cc->camera);
    }
    if (cc->video_frame != NULL) {
        free(cc->video_frame);
    }
    if (cc->device_name != NULL) {
        free(cc->device_name);
    }

    AFREE(cc);
}

/* Creates descriptor for a connecting emulated camera client.
 * Param:
 *  csd - Camera service descriptor.
 *  param - Client parameters. Must be formatted as described in comments to
 *      get_token_value routine, and must contain at least 'name' parameter,
 *      identifiying the camera device to create the service for. Also parameters
 *      may contain a decimal 'inp_channel' parameter, selecting the input
 *      channel to use when communicating with the camera device.
 * Return:
 *  Emulated camera client descriptor on success, or NULL on failure.
 */
static CameraClient*
_camera_client_create(CameraServiceDesc* csd, const char* param)
{
    CameraClient* cc;
    CameraInfo* ci;
    int res;
    ANEW0(cc);

    /*
     * Parse parameter string, containing camera client properties.
     */

    /* Pull required device name. */
    if (get_token_value_alloc(param, "name", &cc->device_name)) {
        E("%s: Allocation failure, or required 'name' parameter is missing, or misformed in '%s'",
          __FUNCTION__, param);
        return NULL;
    }

    /* Pull optional input channel. */
    res = get_token_value_int(param, "inp_channel", &cc->inp_channel);
    if (res != 0) {
        if (res == -1) {
            /* 'inp_channel' parameter has been ommited. Use default input
             * channel, which is zero. */
            cc->inp_channel = 0;
        } else {
            E("%s: 'inp_channel' parameter is misformed in '%s'",
              __FUNCTION__, param);
            return NULL;
        }
    }

    /* Get camera info for the emulated camera represented with this service.
     * Array of camera information records has been created when the camera
     * service was enumerating camera devices during the service initialization.
     * By the camera service protocol, camera service clients must first obtain
     * list of enumerated cameras via the 'list' query to the camera service, and
     * then use device name reported in the list to connect to an emulated camera
     * service. So, if camera information for the given device name is not found
     * in the array, we fail this connection due to protocol violation. */
    ci = _camera_service_get_camera_info_by_device_name(csd, cc->device_name);
    if (ci == NULL) {
        E("%s: Cannot find camera info for device '%s'",
          __FUNCTION__, cc->device_name);
        _camera_client_free(cc);
        return NULL;
    }

    /* We can't allow multiple camera services for a single camera device, Lets
     * make sure that there is no client created for this camera. */
    if (ci->in_use) {
        E("%s: Camera device '%s' is in use", __FUNCTION__, cc->device_name);
        _camera_client_free(cc);
        return NULL;
    }

    /* We're done. Set camera in use, and succeed the connection. */
    ci->in_use = 1;
    cc->camera_info = ci;

    D("%s: Camera service is created for device '%s' using input channel %d",
      __FUNCTION__, cc->device_name, cc->inp_channel);

    return cc;
}

/********************************************************************************
 * Camera client queries
 *******************************************************************************/

/* Client has queried conection to the camera.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. There are no parameters expected for this query.
 */
static void
_camera_client_query_connect(CameraClient* cc, QemudClient* qc, const char* param)
{
    if (cc->camera != NULL) {
        /* Already connected. */
        W("%s: Camera '%s' is already connected", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ok(qc, "Camera is already connected");
        return;
    }

    /* Open camera device. */
    cc->camera = camera_device_open(cc->device_name, cc->inp_channel);
    if (cc->camera == NULL) {
        E("%s: Unable to open camera device '%s'", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Unable to open camera device.");
        return;
    }

    D("%s: Camera device '%s' is now connected", __FUNCTION__, cc->device_name);

    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried disconection from the camera.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. There are no parameters expected for this query.
 */
static void
_camera_client_query_disconnect(CameraClient* cc,
                                QemudClient* qc,
                                const char* param)
{
    if (cc->camera == NULL) {
        /* Already disconnected. */
        W("%s: Camera '%s' is already disconnected", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ok(qc, "Camera is not connected");
        return;
    }

    /* Before we can go ahead and disconnect, we must make sure that camera is
     * not capturing frames. */
    if (cc->video_frame != NULL) {
        E("%s: Cannot disconnect camera '%s' while it is not stopped",
          __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Camera is not stopped");
        return;
    }

    /* Close camera device. */
    camera_device_close(cc->camera);
    cc->camera = NULL;

    D("Camera device '%s' is now disconnected", cc->device_name);

    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried the client to start capturing video.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. Parameters for this query must contain a 'dim', and
 *      a 'pix' parameters, where 'dim' must be "dim=<width>x<height>", and 'pix'
 *      must be "pix=<format>", where 'width' and 'height' must be numerical
 *      values for the capturing video frame width, and height, and 'format' must
 *      be a numerical value for the pixel format of the video frames expected by
 *      the client. 'format' must be one of the V4L2_PIX_FMT_XXX values.
 */
static void
_camera_client_query_start(CameraClient* cc, QemudClient* qc, const char* param)
{
    char* w;
    char dim[64];
    int width, height, pix_format;

    /* Sanity check. */
    if (cc->camera == NULL) {
        /* Not connected. */
        E("%s: Camera '%s' is not connected", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Camera is not connected");
        return;
    }

    /*
     * Parse parameters.
     */

    if (param == NULL) {
        E("%s: Missing parameters for the query", __FUNCTION__);
        _qemu_client_reply_ko(qc, "Missing parameters for the query");
        return;
    }

    /* Pull required 'dim' parameter. */
    if (get_token_value(param, "dim", dim, sizeof(dim))) {
        E("%s: Invalid or missing 'dim' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid or missing 'dim' parameter");
        return;
    }

    /* Pull required 'pix' parameter. */
    if (get_token_value_int(param, "pix", &pix_format)) {
        E("%s: Invalid or missing 'pix' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid or missing 'pix' parameter");
        return;
    }

    /* Parse 'dim' parameter, and get requested frame width and height. */
    w = strchr(dim, 'x');
    if (w == NULL || w[1] == '\0') {
        E("%s: Invalid 'dim' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid 'dim' parameter");
        return;
    }
    *w = '\0'; w++;
    errno = 0;
    width = strtoi(dim, NULL, 10);
    height = strtoi(w, NULL, 10);
    if (errno) {
        E("%s: Invalid 'dim' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid 'dim' parameter");
        return;
    }

    /* After collecting capture parameters lets see if camera has already
     * started, and if so, lets see if parameters match. */
    if (cc->video_frame != NULL) {
        /* Already started. Match capture parameters. */
        if (cc->pixel_format != pix_format ||cc->width != width ||
            cc->height != height) {
            /* Parameters match. Succeed the query. */
            W("%s: Camera '%s' is already started", __FUNCTION__, cc->device_name);
            _qemu_client_reply_ok(qc, "Camera is already started");
        } else {
            /* Parameters don't match. Fail the query. */
            E("%s: Camera '%s' is already started, and parameters don't match:\n"
              "Current %.4s[%dx%d] != requested %.4s[%dx%d]",
              __FUNCTION__, cc->device_name, (const char*)&cc->pixel_format,
              cc->width, cc->height, (const char*)&pix_format, width, height);
            _qemu_client_reply_ko(qc,
                "Camera is already started with different capturing parameters");
        }
        return;
    }

    /*
     * Start the camera.
     */

    /* Save capturing parameters. */
    cc->pixel_format = pix_format;
    cc->width = width;
    cc->height = height;
    cc->pixel_num = cc->width * cc->height;
    cc->frames_cached = 0;

    /* Make sure that pixel format is known, and calculate video framebuffer size
     * along the lines. */
    switch (cc->pixel_format) {
        case V4L2_PIX_FMT_YUV420:
        case V4L2_PIX_FMT_YVU420:
        case V4L2_PIX_FMT_NV12:
        case V4L2_PIX_FMT_NV21:
            cc->video_frame_size = (cc->pixel_num * 12) / 8;
            break;

        default:
            E("%s: Unknown pixel format %.4s",
              __FUNCTION__, (char*)&cc->pixel_format);
            _qemu_client_reply_ko(qc, "Pixel format is unknown");
            return;
    }

    /* Make sure that we have a converters between the original camera pixel
     * format and the one that the client expects. Also a converter must exist
     * for the preview window pixel format (RGB32) */
    if (!has_converter(cc->camera_info->pixel_format, cc->pixel_format) ||
        !has_converter(cc->camera_info->pixel_format, V4L2_PIX_FMT_RGB32)) {
        E("%s: No conversion exist between %.4s and %.4s (or RGB32) pixel formats",
          __FUNCTION__, (char*)&cc->camera_info->pixel_format, (char*)&cc->pixel_format);
        _qemu_client_reply_ko(qc, "No conversion exist for the requested pixel format");
        return;
    }

    /* TODO: At the moment camera framework in the emulator requires RGB32 pixel
     * format for preview window. So, we need to keep two framebuffers here: one
     * for the video, and another for the preview window. Watch out when this
     * changes (if changes). */
    cc->preview_frame_size = cc->pixel_num * 4;

    /* Allocate buffer large enough to contain both, video and preview
     * framebuffers. */
    cc->video_frame =
        (uint8_t*)malloc(cc->video_frame_size + cc->preview_frame_size);
    if (cc->video_frame == NULL) {
        E("%s: Not enough memory for framebuffers %d + %d",
          __FUNCTION__, cc->video_frame_size, cc->preview_frame_size);
        _qemu_client_reply_ko(qc, "Out of memory");
        return;
    }

    /* Set framebuffer pointers. */
    cc->preview_frame = (uint16_t*)(cc->video_frame + cc->video_frame_size);

    /* Start the camera. */
    if (camera_device_start_capturing(cc->camera, cc->camera_info->pixel_format,
                                      cc->width, cc->height)) {
        E("%s: Cannot start camera '%s' for %.4s[%dx%d]: %s",
          __FUNCTION__, cc->device_name, (const char*)&cc->pixel_format,
          cc->width, cc->height, strerror(errno));
        free(cc->video_frame);
        cc->video_frame = NULL;
        _qemu_client_reply_ko(qc, "Cannot start the camera");
        return;
    }

    D("%s: Camera '%s' is now started for %.4s[%dx%d]",
      __FUNCTION__, cc->device_name, (char*)&cc->pixel_format, cc->width,
      cc->height);

    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried the client to stop capturing video.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. There are no parameters expected for this query.
 */
static void
_camera_client_query_stop(CameraClient* cc, QemudClient* qc, const char* param)
{
    if (cc->video_frame == NULL) {
        /* Not started. */
        W("%s: Camera '%s' is not started", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ok(qc, "Camera is not started");
        return;
    }

    /* Stop the camera. */
    if (camera_device_stop_capturing(cc->camera)) {
        E("%s: Cannot stop camera device '%s': %s",
          __FUNCTION__, cc->device_name, strerror(errno));
        _qemu_client_reply_ko(qc, "Cannot stop camera device");
        return;
    }

    free(cc->video_frame);
    cc->video_frame = NULL;

    D("%s: Camera device '%s' is now stopped.", __FUNCTION__, cc->device_name);
    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried next frame.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. Parameters for this query are formatted as such:
 *          video=<size> preview=<size> whiteb=<red>,<green>,<blue> expcomp=<comp>
 *      where:
 *       - 'video', and 'preview' both must be decimal values, defining size of
 *         requested video, and preview frames respectively. Zero value for any
 *         of these parameters means that this particular frame is not requested.
 *       - whiteb contains float values required to calculate whilte balance.
 *       - expcomp contains a float value required to calculate exposure
 *         compensation.
 */
static void
_camera_client_query_frame(CameraClient* cc, QemudClient* qc, const char* param)
{
    int video_size = 0;
    int preview_size = 0;
    int repeat;
    ClientFrameBuffer fbs[2];
    int fbs_num = 0;
    size_t payload_size;
    uint64_t tick;
    float r_scale = 1.0f, g_scale = 1.0f, b_scale = 1.0f, exp_comp = 1.0f;
    char tmp[256];

    /* Sanity check. */
    if (cc->video_frame == NULL) {
        /* Not started. */
        E("%s: Camera '%s' is not started", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Camera is not started");
        return;
    }

    /* Pull required parameters. */
    if (get_token_value_int(param, "video", &video_size) ||
        get_token_value_int(param, "preview", &preview_size)) {
        E("%s: Invalid or missing 'video', or 'preview' parameter in '%s'",
          __FUNCTION__, param);
        _qemu_client_reply_ko(qc,
            "Invalid or missing 'video', or 'preview' parameter");
        return;
    }

    /* Pull white balance values. */
    if (!get_token_value(param, "whiteb", tmp, sizeof(tmp))) {
        if (sscanf(tmp, "%g,%g,%g", &r_scale, &g_scale, &b_scale) != 3) {
            D("Invalid value '%s' for parameter 'whiteb'", tmp);
            r_scale = g_scale = b_scale = 1.0f;
        }
    }

    /* Pull exposure compensation. */
    if (!get_token_value(param, "expcomp", tmp, sizeof(tmp))) {
        if (sscanf(tmp, "%g", &exp_comp) != 1) {
            D("Invalid value '%s' for parameter 'whiteb'", tmp);
            exp_comp = 1.0f;
        }
    }

    /* Verify that framebuffer sizes match the ones that the started camera
     * operates with. */
    if ((video_size != 0 && cc->video_frame_size != video_size) ||
        (preview_size != 0 && cc->preview_frame_size != preview_size)) {
        E("%s: Frame sizes don't match for camera '%s':\n"
          "Expected %d for video, and %d for preview. Requested %d, and %d",
          __FUNCTION__, cc->device_name, cc->video_frame_size,
          cc->preview_frame_size, video_size, preview_size);
        _qemu_client_reply_ko(qc, "Frame size mismatch");
        return;
    }

    /*
     * Initialize framebuffer array for frame read.
     */

    if (video_size) {
        fbs[fbs_num].pixel_format = cc->pixel_format;
        fbs[fbs_num].framebuffer = cc->video_frame;
        fbs_num++;
    }
    if (preview_size) {
        /* TODO: Watch out for preview format changes! */
        fbs[fbs_num].pixel_format = V4L2_PIX_FMT_RGB32;
        fbs[fbs_num].framebuffer = cc->preview_frame;
        fbs_num++;
    }

    /* Capture new frame. */
    tick = _get_timestamp();
    repeat = camera_device_read_frame(cc->camera, fbs, fbs_num,
                                      r_scale, g_scale, b_scale, exp_comp);

    /* Note that there is no (known) way how to wait on next frame being
     * available, so we could dequeue frame buffer from the device only when we
     * know it's available. Instead we're shooting in the dark, and quite often
     * device will response with EAGAIN, indicating that it doesn't have frame
     * ready. In turn, it means that the last frame we have obtained from the
     * device is still good, and we can reply with the cached frames. The only
     * case when we need to keep trying to obtain a new frame is when frame cache
     * is empty. To prevent ourselves from an indefinite loop in case device got
     * stuck on something (observed with some Microsoft devices) we will limit
     * the loop by 2 second time period (which is more than enough to obtain
     * something from the device) */
    while (repeat == 1 && !cc->frames_cached &&
           (_get_timestamp() - tick) < 2000000LL) {
        /* Sleep for 10 millisec before repeating the attempt. */
        _camera_sleep(10);
        repeat = camera_device_read_frame(cc->camera, fbs, fbs_num,
                                          r_scale, g_scale, b_scale, exp_comp);
    }
    if (repeat == 1 && !cc->frames_cached) {
        /* Waited too long for the first frame. */
        E("%s: Unable to obtain first video frame from the camera '%s' in %d milliseconds: %s.",
          __FUNCTION__, cc->device_name,
          (uint32_t)(_get_timestamp() - tick) / 1000, strerror(errno));
        _qemu_client_reply_ko(qc, "Unable to obtain video frame from the camera");
        return;
    } else if (repeat < 0) {
        /* An I/O error. */
        E("%s: Unable to obtain video frame from the camera '%s': %s.",
          __FUNCTION__, cc->device_name, strerror(errno));
        _qemu_client_reply_ko(qc, strerror(errno));
        return;
    }

    /* We have cached something... */
    cc->frames_cached = 1;

    /*
     * Build the reply.
     */

    /* Payload includes "ok:" + requested video and preview frames. */
    payload_size = 3 + video_size + preview_size;

    /* Send payload size first. */
    _qemu_client_reply_payload(qc, payload_size);

    /* After that send the 'ok:'. Note that if there is no frames sent, we should
     * use prefix "ok" instead of "ok:" */
    if (video_size || preview_size) {
        qemud_client_send(qc, (const uint8_t*)"ok:", 3);
    } else {
        /* Still 3 bytes: zero terminator is required in this case. */
        qemud_client_send(qc, (const uint8_t*)"ok", 3);
    }

    /* After that send video frame (if requested). */
    if (video_size) {
        qemud_client_send(qc, cc->video_frame, video_size);
    }

    /* After that send preview frame (if requested). */
    if (preview_size) {
        qemud_client_send(qc, (const uint8_t*)cc->preview_frame, preview_size);
    }
}

/* Handles a message received from the emulated camera client.
 * Queries received here are represented as strings:
 * - 'connect' - Connects to the camera device (opens it).
 * - 'disconnect' - Disconnexts from the camera device (closes it).
 * - 'start' - Starts capturing video from the connected camera device.
 * - 'stop' - Stop capturing video from the connected camera device.
 * - 'frame' - Queries video and preview frames captured from the camera.
 * Param:
 *  opaque - Camera service descriptor.
 *  msg, msglen - Message received from the camera factory client.
 *  client - Camera factory client pipe.
 */
static void
_camera_client_recv(void*         opaque,
                    uint8_t*      msg,
                    int           msglen,
                    QemudClient*  client)
{
    /*
     * Emulated camera client queries.
     */

    /* Connect to the camera. */
    static const char _query_connect[]    = "connect";
    /* Disconnect from the camera. */
    static const char _query_disconnect[] = "disconnect";
    /* Start video capturing. */
    static const char _query_start[]      = "start";
    /* Stop video capturing. */
    static const char _query_stop[]       = "stop";
    /* Query frame(s). */
    static const char _query_frame[]      = "frame";

    char query_name[64];
    const char* query_param = NULL;
    CameraClient* cc = (CameraClient*)opaque;

    /*
     * Emulated camera queries are formatted as such:
     *  "<query name> [<parameters>]"
     */

    T("%s: Camera client query: '%s'", __FUNCTION__, (char*)msg);
    if (_parse_query((const char*)msg, query_name, sizeof(query_name),
        &query_param)) {
        E("%s: Invalid query '%s'", __FUNCTION__, (char*)msg);
        _qemu_client_reply_ko(client, "Invalid query");
        return;
    }

    /* Dispatch the query to an appropriate handler. */
    if (!strcmp(query_name, _query_frame)) {
        /* A frame is queried. */
        _camera_client_query_frame(cc, client, query_param);
    } else if (!strcmp(query_name, _query_connect)) {
        /* Camera connection is queried. */
        _camera_client_query_connect(cc, client, query_param);
    } else if (!strcmp(query_name, _query_disconnect)) {
        /* Camera disnection is queried. */
        _camera_client_query_disconnect(cc, client, query_param);
    } else if (!strcmp(query_name, _query_start)) {
        /* Start capturing is queried. */
        _camera_client_query_start(cc, client, query_param);
    } else if (!strcmp(query_name, _query_stop)) {
        /* Stop capturing is queried. */
        _camera_client_query_stop(cc, client, query_param);
    } else {
        E("%s: Unknown query '%s'", __FUNCTION__, (char*)msg);
        _qemu_client_reply_ko(client, "Unknown query");
    }
}

/* Emulated camera client has been disconnected from the service. */
static void
_camera_client_close(void* opaque)
{
    CameraClient* cc = (CameraClient*)opaque;

    D("%s: Camera client for device '%s' on input channel %d is now closed",
      __FUNCTION__, cc->device_name, cc->inp_channel);

    _camera_client_free(cc);
}

/********************************************************************************
 * Camera service API
 *******************************************************************************/

/* Connects a client to the camera service.
 * There are two classes of the client that can connect to the service:
 *  - Camera factory that is insterested only in listing camera devices attached
 *    to the host.
 *  - Camera device emulators that attach to the actual camera devices.
 * The distinction between these two classes is made by looking at extra
 * parameters passed in client_param variable. If it's NULL, or empty, the client
 * connects to a camera factory. Otherwise, parameters describe the camera device
 * the client wants to connect to.
 */
static QemudClient*
_camera_service_connect(void*          opaque,
                        QemudService*  serv,
                        int            channel,
                        const char*    client_param)
{
    QemudClient*  client = NULL;
    CameraServiceDesc* csd = (CameraServiceDesc*)opaque;

    D("%s: Connecting camera client '%s'",
      __FUNCTION__, client_param ? client_param : "Factory");
    if (client_param == NULL || *client_param == '\0') {
        /* This is an emulated camera factory client. */
        client = qemud_client_new(serv, channel, client_param, csd,
                                  _factory_client_recv, _factory_client_close,
                                  NULL, NULL);
    } else {
        /* This is an emulated camera client. */
        CameraClient* cc = _camera_client_create(csd, client_param);
        if (cc != NULL) {
            client = qemud_client_new(serv, channel, client_param, cc,
                                      _camera_client_recv, _camera_client_close,
                                      NULL, NULL);
        }
    }

    return client;
}

void
android_camera_service_init(void)
{
    static int _inited = 0;

    if (!_inited) {
        _camera_service_init(&_camera_service_desc);
        QemudService*  serv = qemud_service_register( SERVICE_NAME, 0,
                                                      &_camera_service_desc,
                                                      _camera_service_connect,
                                                      NULL, NULL);
        if (serv == NULL) {
            derror("%s: Could not register '%s' service",
                   __FUNCTION__, SERVICE_NAME);
            return;
        }
        D("%s: Registered '%s' qemud service", __FUNCTION__, SERVICE_NAME);
    }
}

void
android_list_web_cameras(void)
{
    CameraInfo ci[MAX_CAMERA];
    int connected_cnt;
    int i;

    /* Enumerate camera devices connected to the host. */
    connected_cnt = enumerate_camera_devices(ci, MAX_CAMERA);
    if (connected_cnt <= 0) {
        return;
    }

    printf("List of web cameras connected to the computer:\n");
    for (i = 0; i < connected_cnt; i++) {
        printf(" Camera '%s' is connected to device '%s' on channel %d using pixel format '%.4s'\n",
               ci[i].display_name, ci[i].device_name, ci[i].inp_channel,
               (const char*)&ci[i].pixel_format);
    }
    printf("\n");
}
