/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//#define ALOG_NDEBUG 0
#define ALOG_NIDEBUG 0
#define LOG_TAG "QCameraUsbParm"
#include <utils/Log.h>

#include <utils/Errors.h>
#include <utils/threads.h>
#include <utils/String16.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cutils/properties.h>
#include <math.h>
#if HAVE_ANDROID_OS
#include <linux/android_pmem.h>
#endif
#include <linux/ioctl.h>
#include <camera/QCameraParameters.h>
#include <media/mediarecorder.h>
#include <gralloc_priv.h>

#include "linux/msm_mdp.h"
#include <linux/fb.h>
#include <limits.h>


extern "C" {
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/system_properties.h>
#include <sys/time.h>
#include <stdlib.h>
#include <linux/msm_ion.h>
#include <camera.h>
#include <cam_fifo.h>
#include <jpege.h>

} // extern "C"

#include "QCameraHWI.h"
#include "QualcommUsbCamera.h"
#include "QCameraUsbPriv.h"
#include "QCameraUsbParm.h"

namespace android {

/********************************************************************/
static const str_map preview_formats[] = {
    {QCameraParameters::PIXEL_FORMAT_YUV420SP, HAL_PIXEL_FORMAT_YCrCb_420_SP},
};

static const preview_format_info_t preview_format_info_list[] = {
    {HAL_PIXEL_FORMAT_YV12, CAMERA_YUV_420_YV12, CAMERA_PAD_TO_WORD, 3}
};

static struct camera_size_type previewSizes[] = {
    { 1920, 1088}, //1080p
    { 1280, 720}, // 720P,
    { 640, 480}, // VGA
    { 512, 384},
    { 480, 320},
    { 320, 240}, // QVGA
};

// All fps ranges which can be supported. This list will be filtered according
// to the min and max fps supported by hardware
// this list must be sorted first by max_fps and then min_fps
// fps values are multiplied by 1000
static android::FPSRange prevFpsRanges[] = {
    android::FPSRange(5000, 121000),
};

/* TBR: Is frame rate mode mandatory */
static const str_map frame_rate_modes[] = {
    {QCameraParameters::KEY_QC_PREVIEW_FRAME_RATE_AUTO_MODE, FPS_MODE_AUTO},
    {QCameraParameters::KEY_QC_PREVIEW_FRAME_RATE_FIXED_MODE, FPS_MODE_FIXED}
};

static const str_map picture_formats[] = {
    {QCameraParameters::PIXEL_FORMAT_JPEG, PICTURE_FORMAT_JPEG},
    //{QCameraParameters::PIXEL_FORMAT_RAW, PICTURE_FORMAT_RAW}
};

static camera_size_type picture_sizes[] = {
    { 1920, 1088}, //HD1080
    { 1280, 720}, //HD720
    { 640, 480}, // VGA
    { 320, 240}, // QVGA
};

/* aspect ratio removed */
static camera_size_type thumbnail_sizes[] = {
    { 512, 288 }, //1.777778
    { 480, 288 }, //1.666667
    { 256, 154 }, //1.66233
    { 432, 288 }, //1.5
    { 512, 384 }, //1.333333
    { 352, 288 }, //1.222222
    { 320, 240 }, //1.33333
    { 176, 144 }, //1.222222
};

static const str_map recording_Hints[] = {
    {"false", FALSE},
    {"true",  TRUE}
};

/* Static functions list */
static String8 create_sizes_str(const camera_size_type *sizes, int len);
static String8 create_values_str(const str_map *values, int len);
static String8 create_fps_str(const android:: FPSRange* fps, int len);
static String8 create_values_range_str(int min, int max);
static int usbCamSetPrvwSize(   camera_hardware_t           *camHal,
                                const QCameraParameters&    params);
static int usbCamSetPictSize(   camera_hardware_t           *camHal,
                                const QCameraParameters&    params);
static int usbCamSetThumbnailSize(  camera_hardware_t           *camHal,
                                    const QCameraParameters&    params);
static int usbCamSetJpegQlty(   camera_hardware_t           *camHal,
                                const QCameraParameters&    params);

/******************************************************************************
 * Function: usbCamInitDefaultParameters
 * Description: This function sets default parameters to camera HAL context
 *
 * Input parameters:
 *   camHal              - camera HAL handle
 *
 * Return values:
 *      None
 *
 * Notes: none
 *****************************************************************************/
int usbCamInitDefaultParameters(camera_hardware_t *camHal)
{
    ALOGD("%s: E", __func__);
    int rc = 0;
    char tempStr[FILENAME_LENGTH];

    /* Default initializations */
    camHal->prevFormat          = DEFAULT_USBCAM_PRVW_FMT;
    camHal->prevWidth           = DEFAULT_USBCAM_PRVW_WD;
    camHal->prevHeight          = DEFAULT_USBCAM_PRVW_HT;
    camHal->dispFormat          = camHal->prevFormat;
    camHal->dispWidth           = camHal->prevWidth;
    camHal->dispHeight          = camHal->prevHeight;
    camHal->pictFormat          = DEFAULT_USBCAM_PICT_FMT;
    camHal->pictWidth           = DEFAULT_USBCAM_PICT_WD;
    camHal->pictHeight          = DEFAULT_USBCAM_PICT_HT;
    camHal->pictJpegQlty        = DEFAULT_USBCAM_PICT_QLTY;
    camHal->thumbnailWidth      = DEFAULT_USBCAM_THUMBNAIL_WD;
    camHal->thumbnailHeight     = DEFAULT_USBCAM_THUMBNAIL_HT;
    camHal->thumbnailJpegQlty   = DEFAULT_USBCAM_THUMBNAIL_QLTY;
    camHal->previewEnabledFlag  = 0;
    camHal->prvwStoppedForPicture = 0;
    camHal->prvwCmdPending      = 0;
    camHal->takePictInProgress  = 0;

    //Set picture size values
    camHal->pictSizeValues = create_sizes_str(
        picture_sizes, sizeof(picture_sizes) / sizeof(camera_size_type));
    camHal->qCamParams.set(QCameraParameters::KEY_SUPPORTED_PICTURE_SIZES,
        camHal->pictSizeValues.string());
    camHal->qCamParams.setPictureSize(camHal->pictWidth, camHal->pictHeight);

    //Set picture format
    camHal->pictFormatValues = create_values_str(
        picture_formats, sizeof(picture_formats) / sizeof(str_map));
    camHal->qCamParams.set(QCameraParameters::KEY_SUPPORTED_PICTURE_FORMATS,
                    camHal->pictFormatValues.string());
    if(PICTURE_FORMAT_JPEG == camHal->pictFormat)
        camHal->qCamParams.setPictureFormat(QCameraParameters::PIXEL_FORMAT_JPEG);

    //Set picture quality
    sprintf(tempStr, "%d", camHal->pictJpegQlty);
    camHal->qCamParams.set(QCameraParameters::KEY_JPEG_QUALITY, tempStr);

    //Set Thumbnail size
    camHal->thumbnailSizeValues = create_sizes_str(
        thumbnail_sizes, sizeof(thumbnail_sizes) /sizeof(camera_size_type));
    camHal->qCamParams.set(QCameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES,
                    camHal->thumbnailSizeValues.string());
    sprintf(tempStr, "%d", camHal->thumbnailWidth);
    camHal->qCamParams.set(QCameraParameters::KEY_JPEG_THUMBNAIL_WIDTH,
                                                tempStr);
    sprintf(tempStr, "%d", camHal->thumbnailHeight);
    camHal->qCamParams.set(QCameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT,
                                                tempStr);

    //Set Thumbnail quality
    sprintf(tempStr, "%d", camHal->thumbnailJpegQlty);
    camHal->qCamParams.set(QCameraParameters::KEY_JPEG_THUMBNAIL_QUALITY,
                                                tempStr);

    //Set Preview Format
    camHal->prevFormatValues = create_values_str(
        preview_formats, sizeof(preview_formats) / sizeof(str_map));
    camHal->qCamParams.set(QCameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS,
        camHal->prevFormatValues.string());
    if(HAL_PIXEL_FORMAT_YCrCb_420_SP == camHal->prevFormat)
        camHal->qCamParams.setPreviewFormat(QCameraParameters::PIXEL_FORMAT_YUV420SP);

    //Set Preview size
    camHal->prevSizeValues = create_sizes_str(
        previewSizes,  sizeof(previewSizes) / sizeof(camera_size_type));
    camHal->qCamParams.set(QCameraParameters::KEY_SUPPORTED_PREVIEW_SIZES,
                    camHal->prevSizeValues.string());
    camHal->qCamParams.setPreviewSize(camHal->prevWidth, camHal->prevHeight);

    //Set Preivew fps range
    camHal->prevFpsRangesValues = create_fps_str(
        prevFpsRanges, sizeof(prevFpsRanges) / sizeof(android::FPSRange));

    camHal->qCamParams.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE,
                        camHal->prevFpsRangesValues);
    camHal->qCamParams.setPreviewFpsRange(MIN_PREV_FPS, MAX_PREV_FPS);

    ALOGD("%s: X", __func__);

    return rc;
} /* usbCamInitDefaultParameters */

/******************************************************************************
 * Function: usbCamSetParameters
 * Description: This function parses the parameter string and stores the
 *              parameters in the camera HAL handle
 *
 * Input parameters:
 *  camHal              - camera HAL handle
 *  params              - pointer to parameter string
 *
 * Return values:
 *      0   Success
 *      -1  Error
 * Notes: none
 *****************************************************************************/
int usbCamSetParameters(camera_hardware_t *camHal, const char *params)
{
    int             rc      = 0;
    String8         str     = String8(params);
    QCameraParameters qParam;

    ALOGD("%s: E", __func__);

    if(params)
        PRINT_PARAM_STR(params);

    qParam.unflatten(str);

    if(usbCamSetPrvwSize(camHal, qParam))
        rc = -1;
    if(usbCamSetPictSize(camHal, qParam))
        rc = -1;
    if(usbCamSetThumbnailSize(camHal, qParam))
        rc = -1;
    if(usbCamSetJpegQlty(camHal, qParam))
        rc = -1;

    ALOGD("%s: X", __func__);
    return rc;
} /* usbCamSetParameters */

/******************************************************************************
 * Function: usbCamGetParameters
 * Description: This function allocates memory for parameter string,
 *              composes and returns the parameter string
 *
 * Input parameters:
 *   camHal             - camera HAL handle
 *
 * Return values:
 *      Address to the parameter string
 *
 * Notes: none
 *****************************************************************************/
char* usbCamGetParameters(camera_hardware_t *camHal)
{
    ALOGD("%s: E", __func__);
    char *parms = NULL;
    char* rc = NULL;
    String8 str;

    QCameraParameters qParam = camHal->qCamParams;
    //qParam.dump();
    str = qParam.flatten( );
    rc = (char *)malloc(sizeof(char)*(str.length()+1));
    if(rc != NULL){
        memset(rc, 0, sizeof(char)*(str.length()+1));
        strncpy(rc, str.string(), str.length());
    rc[str.length()] = 0;
    parms = rc;
    }

    PRINT_PARAM_STR(parms);

    ALOGD("%s: X", __func__);
    return (parms);
} /* usbCamGetParameters */

/******************************************************************************
 * Function: usbCamPutParameters
 * Description: This function frees the memory allocated for parameter string
 *
 * Input parameters:
 *  camHal              - camera HAL handle
 *  parms               - Parameter string
 *
 * Return values:
 *      None
 *
 * Notes: none
 *****************************************************************************/
void usbCamPutParameters(camera_hardware_t *camHal, char *parms)
{
    ALOGD("%s: E", __func__);
    if(parms)
        free(parms);
    parms = NULL;
    ALOGD("%s: X", __func__);
} /* usbCamPutParameters */

/******************************************************************************
 * Function: create_sizes_str
 * Description: This function loops through /dev/video entries and probes with
 *              UVCIOC query. If the device responds to the query, then it is
 *              detected as UVC webcam
 * Input parameters:
 *   devname             - String pointer. The function return dev entry
 *                          name in this string
 * Return values:
 *      0   Success
 *      -1  Error
 * Notes: none
 *****************************************************************************/
static String8 create_sizes_str(const camera_size_type *sizes, int len) {
    String8 str;
    char buffer[32];

    if (len > 0) {
        snprintf(buffer, sizeof(buffer), "%dx%d", sizes[0].width, sizes[0].height);
        str.append(buffer);
    }
    for (int i = 1; i < len; i++) {
        snprintf(buffer, sizeof(buffer), ",%dx%d", sizes[i].width, sizes[i].height);
        str.append(buffer);
    }
    return str;
}

/******************************************************************************
 * Function: create_values_str
 * Description: This function loops through /dev/video entries and probes with
 *              UVCIOC query. If the device responds to the query, then it is
 *              detected as UVC webcam
 * Input parameters:
 *   devname             - String pointer. The function return dev entry
 *                          name in this string
 * Return values:
 *      0   Success
 *      -1  Error
 * Notes: none
 *****************************************************************************/
static String8 create_values_str(const str_map *values, int len) {
    String8 str;

    if (len > 0) {
        str.append(values[0].desc);
    }
    for (int i = 1; i < len; i++) {
        str.append(",");
        str.append(values[i].desc);
    }
    return str;
}

/******************************************************************************
 * Function: create_fps_str
 * Description: This function loops through /dev/video entries and probes with
 *              UVCIOC query. If the device responds to the query, then it is
 *              detected as UVC webcam
 * Input parameters:
 *   devname             - String pointer. The function return dev entry
 *                          name in this string
 * Return values:
 *      0   Success
 *      -1  Error
 * Notes: none
 *****************************************************************************/
static String8 create_fps_str(const android:: FPSRange* fps, int len) {
    String8 str;
    char buffer[32];

    if (len > 0) {
        snprintf(buffer, sizeof(buffer), "(%d,%d)", fps[0].minFPS, fps[0].maxFPS);
        str.append(buffer);
    }
    for (int i = 1; i < len; i++) {
        snprintf(buffer, sizeof(buffer), ",(%d,%d)", fps[i].minFPS, fps[i].maxFPS);
        str.append(buffer);
    }
    return str;
}

/******************************************************************************
 * Function: create_values_range_str
 * Description: This function loops through /dev/video entries and probes with
 *              UVCIOC query. If the device responds to the query, then it is
 *              detected as UVC webcam
 * Input parameters:
 *   devname             - String pointer. The function return dev entry
 *                          name in this string
 * Return values:
 *      0   Success
 *      -1  Error
 * Notes: none
 *****************************************************************************/
static String8 create_values_range_str(int min, int max){
    String8 str;
    char buffer[32];

    if(min <= max){
        snprintf(buffer, sizeof(buffer), "%d", min);
        str.append(buffer);

        for (int i = min + 1; i <= max; i++) {
            snprintf(buffer, sizeof(buffer), ",%d", i);
            str.append(buffer);
        }
    }
    return str;
}

/******************************************************************************
 * Function: usbCamSetPrvwSize
 * Description: This function parses preview width and height from the input
 *              parameters and stores into the context
 *
 * Input parameters:
 *  camHal              - camera HAL handle
 *  params              - QCameraParameters reference
 *
 * Return values:
 *      0   If parameters are valid
 *      -1  If parameters are invalid
 *
 * Notes: none
 *****************************************************************************/
static int usbCamSetPrvwSize(   camera_hardware_t           *camHal,
                                const QCameraParameters&    params)
{
    int rc = 0, width, height, i, numPrvwSizes, validSize;
    ALOGD("%s: E", __func__);

    params.getPreviewSize(&width, &height);
    ALOGI("%s: Requested preview size %d x %d", __func__, width, height);

    // Validate the preview size
    numPrvwSizes = sizeof(previewSizes) / sizeof(camera_size_type);
    for (i = 0, validSize = 0; i <  numPrvwSizes; i++) {
        if (width ==  previewSizes[i].width
           && height ==  previewSizes[i].height) {
            validSize = 1;

            camHal->qCamParams.setPreviewSize(width, height);
            ALOGD("%s: setPreviewSize:  width: %d   height: %d",
                __func__, width, height);

            camHal->prevWidth   = width;
            camHal->prevHeight  = height;
            camHal->dispWidth   = width;
            camHal->dispHeight  = height;

            /* TBD: restrict pictures size and video to preview size */
        }
    }
    if(!validSize)
        ALOGE("%s: Invalid preview size %dx%d requested", __func__,
            width, height);

    rc = (validSize == 0)? -1:0;
    ALOGD("%s: X", __func__);

    return rc;
} /* usbCamSetPrvwSize */

/******************************************************************************
 * Function: usbCamSetPictSize
 * Description: This function parses picture width and height from the input
 *              parameters and stores into the context
 *
 * Input parameters:
 *  camHal              - camera HAL handle
 *  params              - QCameraParameters reference
 *
 * Return values:
 *      0   If parameters are valid
 *      -1  If parameters are invalid
 *
 * Notes: none
 *****************************************************************************/
static int usbCamSetPictSize(   camera_hardware_t           *camHal,
                                const QCameraParameters&    params)
{
    int rc = 0, width, height, i, numPictSizes, validSize;
    ALOGD("%s: E", __func__);

    /* parse for picture width and height */
    params.getPictureSize(&width, &height);
    ALOGI("%s: Requested picture size %d x %d", __func__, width, height);

    // Validate the picture size
    numPictSizes = sizeof(picture_sizes) / sizeof(camera_size_type);
    for (i = 0, validSize = 0; i <  numPictSizes; i++) {
        if (width ==  picture_sizes[i].width
           && height ==  picture_sizes[i].height) {
            validSize = 1;

            camHal->qCamParams.setPictureSize(width, height);
            ALOGD("%s: setPictureSize:  width: %d   height: %d",
                __func__, width, height);

            /* TBD: If new pictSize is different from old size, restart prvw */
            camHal->pictWidth   = width;
            camHal->pictHeight  = height;
        }
    }
    if(!validSize)
        ALOGE("%s: Invalid picture size %dx%d requested", __func__,
            width, height);
    rc = (validSize == 0)? -1:0;
    ALOGD("%s: X", __func__);

    return rc;
} /* usbCamSetPictSize */

/******************************************************************************
 * Function: usbCamSetThumbnailSize
 * Description: This function parses picture width and height from the input
 *              parameters and stores into the context
 *
 * Input parameters:
 *  camHal              - camera HAL handle
 *  params              - QCameraParameters reference
 *
 * Return values:
 *      0   If parameters are valid
 *      -1  If parameters are invalid
 *
 * Notes: none
 *****************************************************************************/
static int usbCamSetThumbnailSize(  camera_hardware_t           *camHal,
                                    const QCameraParameters&    params)
{
    int rc = 0, width, height, i, numThumbnailSizes, validSize;
    char tempStr[FILENAME_LENGTH];
    ALOGD("%s: E", __func__);

    /* parse for thumbnail width and height */
    width = params.getInt(QCameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
    height = params.getInt(QCameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
    ALOGI("%s: Requested thumbnail size %d x %d", __func__, width, height);

    // Validate the thumbnail size
    numThumbnailSizes = sizeof(thumbnail_sizes) / sizeof(camera_size_type);
    for (i = 0, validSize = 0; i <  numThumbnailSizes; i++) {
        if (width ==  thumbnail_sizes[i].width
           && height ==  thumbnail_sizes[i].height) {
            validSize = 1;

            camHal->thumbnailWidth   = width;
            camHal->thumbnailHeight  = height;
            sprintf(tempStr, "%d", camHal->thumbnailWidth);
            camHal->qCamParams.set(QCameraParameters::KEY_JPEG_THUMBNAIL_WIDTH,
                                                        width);
            sprintf(tempStr, "%d", camHal->thumbnailHeight);
            camHal->qCamParams.set(QCameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT,
                                                        height);

        }
    }
    if(!validSize)
        ALOGE("%s: Invalid picture size %dx%d requested", __func__,
            width, height);
    rc = (validSize == 0)? -1:0;
    ALOGD("%s: X", __func__);

    return rc;
} /* usbCamSetThumbnailSize */

/******************************************************************************
 * Function: usbCamSetJpegQlty
 * Description: This function parses picture and thumbnail JPEG quality and
 *              validates before storing in the context
 *
 * Input parameters:
 *  camHal              - camera HAL handle
 *  params              - QCameraParameters reference
 *
 * Return values:
 *      0   If parameters are valid
 *      -1  If parameters are invalid
 *
 * Notes: none
 *****************************************************************************/
static int usbCamSetJpegQlty(   camera_hardware_t           *camHal,
                                const QCameraParameters&    params)
{
    int rc = 0, quality = 0;
    char tempStr[FILENAME_LENGTH];
    ALOGD("%s: E", __func__);

    /**/
    quality = params.getInt(QCameraParameters::KEY_JPEG_QUALITY);
    ALOGI("%s: Requested picture qlty %d", __func__, quality);

    if (quality >= 0 && quality <= 100) {
        camHal->pictJpegQlty = quality;
        sprintf(tempStr, "%d", camHal->pictJpegQlty);
        camHal->qCamParams.set(QCameraParameters::KEY_JPEG_QUALITY, quality);
    } else {
        ALOGE("Invalid jpeg quality=%d", quality);
        rc = -1;
    }

    quality = params.getInt(QCameraParameters::KEY_JPEG_THUMBNAIL_QUALITY);
    ALOGI("%s: Requested thumbnail qlty %d", __func__, quality);

    if (quality >= 0 && quality <= 100) {
        camHal->thumbnailJpegQlty = quality;
        sprintf(tempStr, "%d", camHal->thumbnailJpegQlty);
        camHal->qCamParams.set(QCameraParameters::KEY_JPEG_THUMBNAIL_QUALITY,
                                    tempStr);
    } else {
        ALOGE("Invalid jpeg thumbnail quality=%d", quality);
        rc = -1;
    }

    ALOGD("%s: X rc:%d", __func__, rc);

    return rc;
}

}; /*namespace android */