/* Copyright (c) 2012-2016, 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.
 *
 */

#ifndef MM_JPEG_H_
#define MM_JPEG_H_

// OpenMAX dependencies
#include "OMX_Types.h"
#include "OMX_Index.h"
#include "OMX_Core.h"
#include "OMX_Component.h"
#include "QOMX_JpegExtensions.h"

// JPEG dependencies
#include "mm_jpeg_interface.h"
#include "mm_jpeg_ionbuf.h"

// Camera dependencies
#include "cam_list.h"
#include "cam_semaphore.h"

#define MM_JPEG_MAX_THREADS 30
#define MM_JPEG_CIRQ_SIZE 30
#define MM_JPEG_MAX_SESSION 10
#define MAX_EXIF_TABLE_ENTRIES 50
#define MAX_JPEG_SIZE 20000000
#define MAX_OMX_HANDLES (5)
// Thumbnail src and dest aspect ratio diffrence tolerance
#define ASPECT_TOLERANCE 0.001


/** mm_jpeg_abort_state_t:
 *  @MM_JPEG_ABORT_NONE: Abort is not issued
 *  @MM_JPEG_ABORT_INIT: Abort is issued from the client
 *  @MM_JPEG_ABORT_DONE: Abort is completed
 *
 *  State representing the abort state
 **/
typedef enum {
  MM_JPEG_ABORT_NONE,
  MM_JPEG_ABORT_INIT,
  MM_JPEG_ABORT_DONE,
} mm_jpeg_abort_state_t;


/* define max num of supported concurrent jpeg jobs by OMX engine.
 * Current, only one per time */
#define NUM_MAX_JPEG_CNCURRENT_JOBS 2

#define JOB_ID_MAGICVAL 0x1
#define JOB_HIST_MAX 10000

/** DUMP_TO_FILE:
 *  @filename: file name
 *  @p_addr: address of the buffer
 *  @len: buffer length
 *
 *  dump the image to the file
 **/
#define DUMP_TO_FILE(filename, p_addr, len) ({ \
  size_t rc = 0; \
  FILE *fp = fopen(filename, "w+"); \
  if (fp) { \
    rc = fwrite(p_addr, 1, len, fp); \
    LOGE("written size %zu", len); \
    fclose(fp); \
  } else { \
    LOGE("open %s failed", filename); \
  } \
})

/** DUMP_TO_FILE2:
 *  @filename: file name
 *  @p_addr: address of the buffer
 *  @len: buffer length
 *
 *  dump the image to the file if the memory is non-contiguous
 **/
#define DUMP_TO_FILE2(filename, p_addr1, len1, paddr2, len2) ({ \
  size_t rc = 0; \
  FILE *fp = fopen(filename, "w+"); \
  if (fp) { \
    rc = fwrite(p_addr1, 1, len1, fp); \
    rc = fwrite(p_addr2, 1, len2, fp); \
    LOGE("written %zu %zu", len1, len2); \
    fclose(fp); \
  } else { \
    LOGE("open %s failed", filename); \
  } \
})

/** MM_JPEG_CHK_ABORT:
 *  @p: client pointer
 *  @ret: return value
 *  @label: label to jump to
 *
 *  check the abort failure
 **/
#define MM_JPEG_CHK_ABORT(p, ret, label) ({ \
  if (MM_JPEG_ABORT_INIT == p->abort_state) { \
    LOGE("jpeg abort"); \
    ret = OMX_ErrorNone; \
    goto label; \
  } \
})

#define GET_CLIENT_IDX(x) ((x) & 0xff)
#define GET_SESSION_IDX(x) (((x) >> 8) & 0xff)
#define GET_JOB_IDX(x) (((x) >> 16) & 0xff)

typedef struct {
  union {
    int i_data[MM_JPEG_CIRQ_SIZE];
    void *p_data[MM_JPEG_CIRQ_SIZE];
  };
  int front;
  int rear;
  int count;
  pthread_mutex_t lock;
} mm_jpeg_cirq_t;

/** cirq_reset:
 *
 *  Arguments:
 *    @q: circular queue
 *
 *  Return:
 *       none
 *
 *  Description:
 *       Resets the circular queue
 *
 **/
static inline void cirq_reset(mm_jpeg_cirq_t *q)
{
  q->front = 0;
  q->rear = 0;
  q->count = 0;
  pthread_mutex_init(&q->lock, NULL);
}

/** cirq_empty:
 *
 *  Arguments:
 *    @q: circular queue
 *
 *  Return:
 *       none
 *
 *  Description:
 *       check if the curcular queue is empty
 *
 **/
#define cirq_empty(q) (q->count == 0)

/** cirq_full:
 *
 *  Arguments:
 *    @q: circular queue
 *
 *  Return:
 *       none
 *
 *  Description:
 *       check if the curcular queue is full
 *
 **/
#define cirq_full(q) (q->count == MM_JPEG_CIRQ_SIZE)

/** cirq_enqueue:
 *
 *  Arguments:
 *    @q: circular queue
 *    @data: data to be inserted
 *
 *  Return:
 *       true/false
 *
 *  Description:
 *       enqueue an element into circular queue
 *
 **/
#define cirq_enqueue(q, type, data) ({ \
  int rc = 0; \
  pthread_mutex_lock(&q->lock); \
  if (cirq_full(q)) { \
    rc = -1; \
  } else { \
    q->type[q->rear] = data; \
    q->rear = (q->rear + 1) % MM_JPEG_CIRQ_SIZE; \
    q->count++; \
  } \
  pthread_mutex_unlock(&q->lock); \
  rc; \
})

/** cirq_dequeue:
 *
 *  Arguments:
 *    @q: circular queue
 *    @data: data to be popped
 *
 *  Return:
 *       true/false
 *
 *  Description:
 *       dequeue an element from the circular queue
 *
 **/
#define cirq_dequeue(q, type, data) ({ \
  int rc = 0; \
  pthread_mutex_lock(&q->lock); \
  if (cirq_empty(q)) { \
    pthread_mutex_unlock(&q->lock); \
    rc = -1; \
  } else { \
    data = q->type[q->front]; \
    q->count--; \
  } \
  pthread_mutex_unlock(&q->lock); \
  rc; \
})


typedef union {
  uint32_t u32;
  void* p;
} mm_jpeg_q_data_t;

  typedef struct {
  struct cam_list list;
  mm_jpeg_q_data_t data;
} mm_jpeg_q_node_t;

typedef struct {
  mm_jpeg_q_node_t head; /* dummy head */
  uint32_t size;
  pthread_mutex_t lock;
} mm_jpeg_queue_t;

typedef enum {
  MM_JPEG_CMD_TYPE_JOB,          /* job cmd */
  MM_JPEG_CMD_TYPE_EXIT,         /* EXIT cmd for exiting jobMgr thread */
  MM_JPEG_CMD_TYPE_DECODE_JOB,
  MM_JPEG_CMD_TYPE_MAX
} mm_jpeg_cmd_type_t;

typedef struct mm_jpeg_job_session {
  uint32_t client_hdl;           /* client handler */
  uint32_t jobId;                /* job ID */
  uint32_t sessionId;            /* session ID */
  mm_jpeg_encode_params_t params; /* encode params */
  mm_jpeg_decode_params_t dec_params; /* encode params */
  mm_jpeg_encode_job_t encode_job;             /* job description */
  mm_jpeg_decode_job_t decode_job;
  pthread_t encode_pid;          /* encode thread handler*/

  void *jpeg_obj;                /* ptr to mm_jpeg_obj */
  jpeg_job_status_t job_status;  /* job status */

  int state_change_pending;      /* flag to indicate if state change is pending */
  OMX_ERRORTYPE error_flag;      /* variable to indicate error during encoding */
  mm_jpeg_abort_state_t abort_state; /* variable to indicate abort during encoding */

  /* OMX related */
  OMX_HANDLETYPE omx_handle;                      /* handle to omx engine */
  OMX_CALLBACKTYPE omx_callbacks;                 /* callbacks to omx engine */

  /* buffer headers */
  OMX_BUFFERHEADERTYPE *p_in_omx_buf[MM_JPEG_MAX_BUF];
  OMX_BUFFERHEADERTYPE *p_in_omx_thumb_buf[MM_JPEG_MAX_BUF];
  OMX_BUFFERHEADERTYPE *p_out_omx_buf[MM_JPEG_MAX_BUF];

  OMX_PARAM_PORTDEFINITIONTYPE inputPort;
  OMX_PARAM_PORTDEFINITIONTYPE outputPort;
  OMX_PARAM_PORTDEFINITIONTYPE inputTmbPort;

  /* event locks */
  pthread_mutex_t lock;
  pthread_cond_t cond;

  QEXIF_INFO_DATA exif_info_local[MAX_EXIF_TABLE_ENTRIES];  //all exif tags for JPEG encoder
  int exif_count_local;

  mm_jpeg_cirq_t cb_q;
  int32_t ebd_count;
  int32_t fbd_count;

  /* this flag represents whether the job is active */
  OMX_BOOL active;

  /* this flag indicates if the configration is complete */
  OMX_BOOL config;

  /* job history count to generate unique id */
  unsigned int job_hist;

  OMX_BOOL encoding;

  buffer_t work_buffer;

  OMX_EVENTTYPE omxEvent;
  int event_pending;

  uint8_t *meta_enc_key;
  size_t meta_enc_keylen;

  struct mm_jpeg_job_session *next_session;

  uint32_t curr_out_buf_idx;

  uint32_t num_omx_sessions;
  OMX_BOOL auto_out_buf;

  mm_jpeg_queue_t *session_handle_q;
  mm_jpeg_queue_t *out_buf_q;

  int thumb_from_main;
  uint32_t job_index;
} mm_jpeg_job_session_t;

typedef struct {
  mm_jpeg_encode_job_t encode_job;
  uint32_t job_id;
  uint32_t client_handle;
} mm_jpeg_encode_job_info_t;

typedef struct {
  mm_jpeg_decode_job_t decode_job;
  uint32_t job_id;
  uint32_t client_handle;
} mm_jpeg_decode_job_info_t;

typedef struct {
  mm_jpeg_cmd_type_t type;
  union {
    mm_jpeg_encode_job_info_t enc_info;
    mm_jpeg_decode_job_info_t dec_info;
  };
} mm_jpeg_job_q_node_t;

typedef struct {
  uint8_t is_used;                /* flag: if is a valid client */
  uint32_t client_handle;         /* client handle */
  mm_jpeg_job_session_t session[MM_JPEG_MAX_SESSION];
  pthread_mutex_t lock;           /* job lock */
} mm_jpeg_client_t;

typedef struct {
  pthread_t pid;                  /* job cmd thread ID */
  cam_semaphore_t job_sem;        /* semaphore for job cmd thread */
  mm_jpeg_queue_t job_queue;      /* queue for job to do */
} mm_jpeg_job_cmd_thread_t;

#define MAX_JPEG_CLIENT_NUM 8
typedef struct mm_jpeg_obj_t {
  /* ClientMgr */
  int num_clients;                                /* num of clients */
  mm_jpeg_client_t clnt_mgr[MAX_JPEG_CLIENT_NUM]; /* client manager */

  /* JobMkr */
  pthread_mutex_t job_lock;                       /* job lock */
  mm_jpeg_job_cmd_thread_t job_mgr;               /* job mgr thread including todo_q*/
  mm_jpeg_queue_t ongoing_job_q;                  /* queue for ongoing jobs */
  buffer_t ionBuffer[MM_JPEG_CONCURRENT_SESSIONS_COUNT];


  /* Max pic dimension for work buf calc*/
  uint32_t max_pic_w;
  uint32_t max_pic_h;
#ifdef LOAD_ADSP_RPC_LIB
  void *adsprpc_lib_handle;
#endif

  uint32_t work_buf_cnt;

  uint32_t num_sessions;
  uint32_t reuse_reproc_buffer;

  cam_jpeg_metadata_t *jpeg_metadata;

  /* Pointer to the session in progress*/
  mm_jpeg_job_session_t *p_session_inprogress;

  // dummy OMX handle
  OMX_HANDLETYPE dummy_handle;
} mm_jpeg_obj;

/** mm_jpeg_pending_func_t:
 *
 * Intermediate function for transition change
 **/
typedef OMX_ERRORTYPE (*mm_jpeg_transition_func_t)(void *);

extern int32_t mm_jpeg_init(mm_jpeg_obj *my_obj);
extern int32_t mm_jpeg_deinit(mm_jpeg_obj *my_obj);
extern uint32_t mm_jpeg_new_client(mm_jpeg_obj *my_obj);
extern int32_t mm_jpeg_start_job(mm_jpeg_obj *my_obj,
  mm_jpeg_job_t* job,
  uint32_t* jobId);
extern int32_t mm_jpeg_abort_job(mm_jpeg_obj *my_obj,
  uint32_t jobId);
extern int32_t mm_jpeg_close(mm_jpeg_obj *my_obj,
  uint32_t client_hdl);
extern int32_t mm_jpeg_create_session(mm_jpeg_obj *my_obj,
  uint32_t client_hdl,
  mm_jpeg_encode_params_t *p_params,
  uint32_t* p_session_id);
extern int32_t mm_jpeg_destroy_session_by_id(mm_jpeg_obj *my_obj,
  uint32_t session_id);

extern int32_t mm_jpegdec_init(mm_jpeg_obj *my_obj);
extern int32_t mm_jpegdec_deinit(mm_jpeg_obj *my_obj);
extern int32_t mm_jpeg_jobmgr_thread_release(mm_jpeg_obj * my_obj);
extern int32_t mm_jpeg_jobmgr_thread_launch(mm_jpeg_obj *my_obj);
extern int32_t mm_jpegdec_start_decode_job(mm_jpeg_obj *my_obj,
  mm_jpeg_job_t* job,
  uint32_t* jobId);

extern int32_t mm_jpegdec_create_session(mm_jpeg_obj *my_obj,
  uint32_t client_hdl,
  mm_jpeg_decode_params_t *p_params,
  uint32_t* p_session_id);

extern int32_t mm_jpegdec_destroy_session_by_id(mm_jpeg_obj *my_obj,
  uint32_t session_id);

extern int32_t mm_jpegdec_abort_job(mm_jpeg_obj *my_obj,
  uint32_t jobId);

int32_t mm_jpegdec_process_decoding_job(mm_jpeg_obj *my_obj,
    mm_jpeg_job_q_node_t* job_node);

/* utiltity fucntion declared in mm-camera-inteface2.c
 * and need be used by mm-camera and below*/
uint32_t mm_jpeg_util_generate_handler(uint8_t index);
uint8_t mm_jpeg_util_get_index_by_handler(uint32_t handler);

/* basic queue functions */
extern int32_t mm_jpeg_queue_init(mm_jpeg_queue_t* queue);
extern int32_t mm_jpeg_queue_enq(mm_jpeg_queue_t* queue,
    mm_jpeg_q_data_t data);
extern int32_t mm_jpeg_queue_enq_head(mm_jpeg_queue_t* queue,
    mm_jpeg_q_data_t data);
extern mm_jpeg_q_data_t mm_jpeg_queue_deq(mm_jpeg_queue_t* queue);
extern int32_t mm_jpeg_queue_deinit(mm_jpeg_queue_t* queue);
extern int32_t mm_jpeg_queue_flush(mm_jpeg_queue_t* queue);
extern uint32_t mm_jpeg_queue_get_size(mm_jpeg_queue_t* queue);
extern mm_jpeg_q_data_t mm_jpeg_queue_peek(mm_jpeg_queue_t* queue);
extern int32_t addExifEntry(QOMX_EXIF_INFO *p_exif_info, exif_tag_id_t tagid,
  exif_tag_type_t type, uint32_t count, void *data);
extern int32_t releaseExifEntry(QEXIF_INFO_DATA *p_exif_data);
extern int process_meta_data(metadata_buffer_t *p_meta,
  QOMX_EXIF_INFO *exif_info, mm_jpeg_exif_params_t *p_cam3a_params,
  cam_hal_version_t hal_version);

OMX_ERRORTYPE mm_jpeg_session_change_state(mm_jpeg_job_session_t* p_session,
  OMX_STATETYPE new_state,
  mm_jpeg_transition_func_t p_exec);

int map_jpeg_format(mm_jpeg_color_format color_fmt);

OMX_BOOL mm_jpeg_session_abort(mm_jpeg_job_session_t *p_session);
/**
 *
 * special queue functions for job queue
 **/
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_client_id(
  mm_jpeg_queue_t* queue, uint32_t client_hdl);
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_job_id(
  mm_jpeg_queue_t* queue, uint32_t job_id);
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_session_id(
  mm_jpeg_queue_t* queue, uint32_t session_id);
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_unlk(
  mm_jpeg_queue_t* queue, uint32_t job_id);


/** mm_jpeg_queue_func_t:
 *
 * Intermediate function for queue operation
 **/
typedef void (*mm_jpeg_queue_func_t)(void *);

/** mm_jpeg_exif_flash_mode:
 *
 * Exif flash mode values
 **/
typedef enum {
  MM_JPEG_EXIF_FLASH_MODE_ON   = 0x1,
  MM_JPEG_EXIF_FLASH_MODE_OFF  = 0x2,
  MM_JPEG_EXIF_FLASH_MODE_AUTO = 0x3,
  MM_JPEG_EXIF_FLASH_MODE_MAX
} mm_jpeg_exif_flash_mode;

#endif /* MM_JPEG_H_ */