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

// System dependencies
#include <errno.h>

// Camera dependencies
#include "mm_qcamera_socket.h"
#include "mm_qcamera_commands.h"
#include "mm_qcamera_dbg.h"

#define IP_ADDR                  "127.0.0.1"
#define TUNING_CHROMATIX_PORT     55555
#define TUNING_PREVIEW_PORT       55556

#define CURRENT_COMMAND_ACK_SUCCESS 1
#define CURRENT_COMMAND_ACK_FAILURE 2

pthread_t eztune_thread_id;

static ssize_t tuneserver_send_command_rsp(tuningserver_t *tsctrl,
  char *send_buf, uint32_t send_len)
{
  ssize_t rc;

  /* send ack back to client upon req */
  if (send_len <= 0) {
    LOGE("Invalid send len \n");
    return -1;
  }
  if (send_buf == NULL) {
    LOGE("Invalid send buf \n");
    return -1;
  }

  rc = send(tsctrl->clientsocket_id, send_buf, send_len, 0);
  if (rc < 0) {
    LOGE("RSP send returns error %s\n",  strerror(errno));
  } else {
    rc = 0;
  }

  if (send_buf != NULL) {
    free(send_buf);
    send_buf = NULL;
  }
  return rc;
}

static void release_eztune_prevcmd_rsp(eztune_prevcmd_rsp *pHead)
{
  if (pHead != NULL ) {
    release_eztune_prevcmd_rsp((eztune_prevcmd_rsp *)pHead->next);
    free(pHead);
  }
}

static ssize_t tuneserver_ack(uint16_t a, uint32_t b, tuningserver_t *tsctrl)
{
  ssize_t rc;
  char ack_1[6];
  /*Ack the command here*/
  memcpy(ack_1, &a, 2);
  memcpy(ack_1+2, &b, 4);
  /* send echo back to client upon accept */
  rc = send(tsctrl->clientsocket_id, &ack_1, sizeof(ack_1), 0);
  if (rc < 0) {
    LOGE(" eztune_server_run: send returns error %s\n",
      strerror(errno));
    return rc;
  } else if (rc < (int32_t)sizeof(ack_1)) {
    /*Shouldn't hit this for packets <1K; need to re-send if we do*/
  }
  return 0;
}

static ssize_t tuneserver_send_command_ack( uint8_t ack,
    tuningserver_t *tsctrl)
{
  ssize_t rc;
  /* send ack back to client upon req */
  rc = send(tsctrl->clientsocket_id, &ack, sizeof(ack), 0);
  if (rc < 0) {
    LOGE("ACK send returns error %s\n",  strerror(errno));
    return rc;
  }
  return 0;
}

/** tuneserver_process_command
 *    @tsctrl: the server control object
 *
 *  Processes the command that the client sent
 *
 *  Return: >=0 on success, -1 on failure.
 **/
static int32_t tuneserver_process_command(tuningserver_t *tsctrl,
  char *send_buf, uint32_t send_len)
{
  tuneserver_protocol_t *p = tsctrl->proto;
  int result = 0;

  LOGD(" Current command is %d\n",  p->current_cmd);
  switch (p->current_cmd) {
  case TUNESERVER_GET_LIST:
    if(tuneserver_send_command_ack(CURRENT_COMMAND_ACK_SUCCESS, tsctrl)) {
      LOGE(" Ack Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    result = tuneserver_process_get_list_cmd(tsctrl, p->recv_buf,
      send_buf, send_len);
    if (result < 0) {
      LOGE(" RSP processing Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    if(tuneserver_send_command_rsp(tsctrl, send_buf, send_len)) {
      LOGE(" RSP Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    break;

  case TUNESERVER_GET_PARMS:
    if(tuneserver_send_command_ack(CURRENT_COMMAND_ACK_SUCCESS, tsctrl)) {
      LOGE(" Ack Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    result = tuneserver_process_get_params_cmd(tsctrl, p->recv_buf,
      send_buf, send_len);
    if (result < 0) {
      LOGE(" RSP processing Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    if(tuneserver_send_command_rsp(tsctrl, send_buf, send_len)) {
      LOGE(" RSP Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    break;

  case TUNESERVER_SET_PARMS:
    if(tuneserver_send_command_ack(CURRENT_COMMAND_ACK_SUCCESS, tsctrl)) {
      LOGE(" Ack Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    result = tuneserver_process_set_params_cmd(tsctrl, p->recv_buf,
      send_buf, send_len);
    if (result < 0) {
      LOGE(" RSP processing Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    if(tuneserver_send_command_rsp(tsctrl, send_buf, send_len)) {
      LOGE(" RSP Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    break;

  case TUNESERVER_MISC_CMDS: {
    if(tuneserver_send_command_ack(CURRENT_COMMAND_ACK_SUCCESS, tsctrl)) {
      LOGE(" Ack Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    result = tuneserver_process_misc_cmd(tsctrl, p->recv_buf,
      send_buf, send_len);
    if (result < 0) {
      LOGE(" RSP processing Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    if(tuneserver_send_command_rsp(tsctrl, send_buf, send_len)) {
      LOGE(" RSP Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    break;
  }

  default:
    if(tuneserver_send_command_ack(CURRENT_COMMAND_ACK_SUCCESS, tsctrl)) {
      LOGE(" Ack Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    LOGE(" p->current_cmd: default\n");
    result = -1;
    break;
  }

  return result;
}

/** tuneserver_process_client_message
 *    @recv_buffer: received message from the client
 *    @tsctrl: the server control object
 *
 *  Processes the message from client and prepares for next
 *  message.
 *
 *  Return: >=0 on success, -1 on failure.
 **/
static int32_t tuneserver_process_client_message(void *recv_buffer,
  tuningserver_t *tsctrl)
{
  int rc = 0;
  tuneserver_protocol_t *p = tsctrl->proto;

  switch (tsctrl->proto->next_recv_code) {
  case TUNESERVER_RECV_COMMAND:
    p->current_cmd = *(uint16_t *)recv_buffer;
    p->next_recv_code = TUNESERVER_RECV_PAYLOAD_SIZE;
    p->next_recv_len = sizeof(uint32_t);
    break;

  case TUNESERVER_RECV_PAYLOAD_SIZE:
    p->next_recv_code = TUNESERVER_RECV_PAYLOAD;
    p->next_recv_len = *(uint32_t *)recv_buffer;
    p->recv_len = p->next_recv_len;
    if (p->next_recv_len > TUNESERVER_MAX_RECV)
      return -1;
    if (p->next_recv_len == 0) {
      p->next_recv_code = TUNESERVER_RECV_RESPONSE;
      p->next_recv_len = sizeof(uint32_t);
    }
    break;

  case TUNESERVER_RECV_PAYLOAD:
    p->recv_buf = malloc(p->next_recv_len);
    if (!p->recv_buf) {
      LOGE("Error allocating memory for recv_buf %s\n",
        strerror(errno));
      return -1;
    }
    memcpy(p->recv_buf, recv_buffer, p->next_recv_len);
    p->next_recv_code = TUNESERVER_RECV_RESPONSE;
    p->next_recv_len = sizeof(uint32_t);
    /*Process current command at this point*/
    break;

  case TUNESERVER_RECV_RESPONSE:
    p->next_recv_code = TUNESERVER_RECV_COMMAND;
    p->next_recv_len = 2;
    p->send_len = *(uint32_t *)recv_buffer;
    p->send_buf =  (char *)calloc(p->send_len, sizeof(char *));
    if (!p->send_buf) {
      LOGE("Error allocating memory for send_buf %s\n",
        strerror(errno));
      return -1;
    }
    rc = tuneserver_process_command(tsctrl, p->send_buf, p->send_len);
    free(p->recv_buf);
    p->recv_buf = NULL;
    p->recv_len = 0;
    break;

  default:
    LOGE(" p->next_recv_code: default\n");
    rc = -1;
    break;
  }

  return rc;
}

/** tuneserver_ack_onaccept_initprotocol
 *    @tsctrl: the server control object
 *
 *  Acks a connection from the cient and sets up the
 *  protocol object to start receiving commands.
 *
 *  Return: >=0 on success, -1 on failure.
 **/
static ssize_t tuneserver_ack_onaccept_initprotocol(tuningserver_t *tsctrl)
{
  ssize_t rc = 0;
  uint32_t ack_status;

  LOGE("starts\n");
/*
  if(tsctrl->camera_running) {
    ack_status = 1;
  } else {
    ack_status = 2;
  }
*/
  ack_status = 1;

  rc = tuneserver_ack(1, ack_status, tsctrl);

  tsctrl->proto = malloc(sizeof(tuneserver_protocol_t));
  if (!tsctrl->proto) {
    LOGE(" malloc returns NULL with error %s\n",  strerror(errno));
    return -1;
  }

  tsctrl->proto->current_cmd    = 0xFFFF;
  tsctrl->proto->next_recv_code = TUNESERVER_RECV_COMMAND;
  tsctrl->proto->next_recv_len  = 2;
  tsctrl->proto->recv_buf       = NULL;
  tsctrl->proto->send_buf       = NULL;

  LOGD("X\n");

  return rc;
}

/** tuneserver_check_status
 *    @tsctrl: the server control object
 *
 *  Checks if camera is running and stops it.
 *
 *  Return: >=0 on success, -1 on failure.
 **/
#if 0
static void tuneserver_check_status(tuningserver_t *tsctrl)
{
  if (tsctrl->camera_running == 1) {
    /*TODO: Stop camera here*/
    tuneserver_stop_cam(&tsctrl->lib_handle);
  }
  tsctrl->camera_running = 0;

  tuneserver_close_cam(&tsctrl->lib_handle);
}
#endif

static ssize_t prevserver_send_command_rsp(tuningserver_t *tsctrl,
  char *send_buf, uint32_t send_len)
{
  ssize_t rc;

  /* send ack back to client upon req */
  if (send_len <= 0) {
    LOGE("Invalid send len \n");
    return -1;
  }
  if (send_buf == NULL) {
    LOGE("Invalid send buf \n");
    return -1;
  }

  rc = send(tsctrl->pr_clientsocket_id, send_buf, send_len, 0);
  if (rc < 0) {
    LOGE("RSP send returns error %s\n",  strerror(errno));
  } else {
    rc = 0;
  }
  if (send_buf != NULL) {
    free(send_buf);
    send_buf = NULL;
  }
  return rc;
}

static void prevserver_init_protocol(tuningserver_t *tsctrl)
{
  tsctrl->pr_proto = malloc(sizeof(prserver_protocol_t));
  if (!tsctrl->pr_proto) {
    LOGE(" malloc returns NULL with error %s\n",
      strerror(errno));
    return;
  }

  tsctrl->pr_proto->current_cmd    = 0xFFFF;
  tsctrl->pr_proto->next_recv_code = TUNE_PREV_RECV_COMMAND;
  tsctrl->pr_proto->next_recv_len  = 2;
}

static int32_t prevserver_process_command(
  tuningserver_t *tsctrl, char **send_buf, uint32_t *send_len)
{
  prserver_protocol_t *p = tsctrl->pr_proto;
  int result = 0;
  eztune_prevcmd_rsp *rsp_ptr=NULL, *rspn_ptr=NULL, *head_ptr=NULL;

  LOGD(" Current command is %d\n",  p->current_cmd);
  switch (p->current_cmd) {
  case TUNE_PREV_GET_INFO:
    result = tuneserver_preview_getinfo(tsctrl, send_buf, send_len);
    if (result < 0) {
      LOGE(" RSP processing Failed for cmd %d\n",
        p->current_cmd);
      return -1;
    }
    rsp_ptr = (eztune_prevcmd_rsp *)*send_buf;
    if ((!rsp_ptr) || (!rsp_ptr->send_buf)) {
      LOGE(" RSP ptr is NULL %d\n",  p->current_cmd);
      return -1;
    }
    if (prevserver_send_command_rsp(tsctrl,
      rsp_ptr->send_buf, rsp_ptr->send_len)) {
      LOGE(" RSP Failed for TUNE_PREV_GET_INFO ver cmd %d\n",
        p->current_cmd);
      return -1;
    }
    rspn_ptr = (eztune_prevcmd_rsp *)rsp_ptr->next;
    if ((!rspn_ptr) || (!rspn_ptr->send_buf)) {
      LOGE(" RSP1 ptr is NULL %d\n",  p->current_cmd);
      return -1;
    }
    if (prevserver_send_command_rsp(tsctrl,
        rspn_ptr->send_buf, rspn_ptr->send_len)) {
      LOGE(" RSP Failed for TUNE_PREV_GET_INFO caps cmd %d\n",
        p->current_cmd);
      return -1;
    }
    free(rspn_ptr);
    free(rsp_ptr);
    break;

  case TUNE_PREV_CH_CNK_SIZE:
    result = tuneserver_preview_getchunksize(tsctrl, send_buf, send_len);
    if (result < 0) {
      LOGE(" RSP processing Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    if (prevserver_send_command_rsp(tsctrl, *send_buf, *send_len)) {
      LOGE(" RSP Failed for TUNE_PREV_CH_CNK_SIZE cmd %d\n",
        p->current_cmd);
      return -1;
    }
    break;

  case TUNE_PREV_GET_PREV_FRAME:
    result = tuneserver_preview_getframe(tsctrl, send_buf, send_len);
    if (result < 0) {
      LOGE(" RSP processing Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    rsp_ptr = (eztune_prevcmd_rsp *)*send_buf;
    if ((!rsp_ptr) || (!rsp_ptr->send_buf)) {
      LOGE(" RSP ptr is NULL %d\n",  p->current_cmd);
      return -1;
    }
    head_ptr = rsp_ptr;

    while (rsp_ptr != NULL) {
      if ((!rsp_ptr) || (!rsp_ptr->send_buf)) {
        LOGE(" RSP ptr is NULL %d\n",  p->current_cmd);
        return -1;
      }
      if (prevserver_send_command_rsp(tsctrl,
        rsp_ptr->send_buf, rsp_ptr->send_len)) {
        LOGE(" RSP Failed for TUNE_PREV_GET_INFO ver cmd %d\n",
          p->current_cmd);
        return -1;
      }
      rsp_ptr = (eztune_prevcmd_rsp *)rsp_ptr->next;
    }
    release_eztune_prevcmd_rsp(head_ptr);
    break;

  case TUNE_PREV_GET_JPG_SNAP:
  case TUNE_PREV_GET_RAW_SNAP:
  case TUNE_PREV_GET_RAW_PREV:
    result = tuneserver_preview_unsupported(tsctrl, send_buf, send_len);
    if (result < 0) {
       LOGE("RSP processing Failed for cmd %d\n",  p->current_cmd);
      return -1;
    }
    if (prevserver_send_command_rsp(tsctrl, *send_buf, *send_len)) {
      LOGE("RSP Failed for UNSUPPORTED cmd %d\n",  p->current_cmd);
      return -1;
    }
    break;

  default:
    LOGE(" p->current_cmd: default\n");
    result = -1;
    break;
  }

  return result;
}

/** previewserver_process_client_message
 *    @recv_buffer: received message from the client
 *    @tsctrl: the server control object
 *
 *  Processes the message from client and prepares for next
 *  message.
 *
 *  Return: >=0 on success, -1 on failure.
 **/
static int32_t prevserver_process_client_message(void *recv_buffer,
  tuningserver_t *tsctrl)
{
  int rc = 0;
  prserver_protocol_t *p = tsctrl->pr_proto;

  LOGD("command = %d", p->next_recv_code);

  switch (p->next_recv_code) {
  case TUNE_PREV_RECV_COMMAND:
    p->current_cmd = *(uint16_t *)recv_buffer;
    if(p->current_cmd != TUNE_PREV_CH_CNK_SIZE) {
      rc = prevserver_process_command(tsctrl,
        &p->send_buf, (uint32_t *)&p->send_len);
      break;
    }
    p->next_recv_code = TUNE_PREV_RECV_NEWCNKSIZE;
    p->next_recv_len = sizeof(uint32_t);
    LOGD("TUNE_PREV_COMMAND X\n");
    break;
  case TUNE_PREV_RECV_NEWCNKSIZE:
    p->new_cnk_size = *(uint32_t *)recv_buffer;
    p->next_recv_code = TUNE_PREV_RECV_COMMAND;
    p->next_recv_len  = 2;
    rc = prevserver_process_command(tsctrl,
      &p->send_buf, (uint32_t *)&p->send_len);
    break;
  default:
    LOGE("prev_proc->next_recv_code: default\n");
    rc = -1;
    break;
  }

  return rc;
}

/** tunning_server_socket_listen
 *    @ip_addr: the ip addr to listen
 *    @port: the port to listen
 *
 *  Setup a listen socket for eztune.
 *
 *  Return: >0 on success, <=0 on failure.
 **/
int tunning_server_socket_listen(const char* ip_addr, uint16_t port)
{
  int sock_fd = -1;
  mm_qcamera_sock_addr_t server_addr;
  int result;
  int option;
  int socket_flag;

  memset(&server_addr, 0, sizeof(server_addr));
  server_addr.addr_in.sin_family = AF_INET;
  server_addr.addr_in.sin_port = (__be16) htons(port);
  server_addr.addr_in.sin_addr.s_addr = inet_addr(ip_addr);

  if (server_addr.addr_in.sin_addr.s_addr == INADDR_NONE) {
    LOGE(" invalid address.\n");
    return -1;
  }

  /* Create an AF_INET stream socket to receive incoming connection ON */
  sock_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (sock_fd < 0) {
    LOGE(" socket failed\n");
    return sock_fd;
  }

  // set listen socket to non-block, but why??
  socket_flag = fcntl(sock_fd, F_GETFL, 0);
  fcntl(sock_fd, F_SETFL, socket_flag | O_NONBLOCK);

  /* reuse in case it is in timeout */
  option = 1;
  result = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
    &option, sizeof(option));

  if (result < 0) {
    LOGE("eztune setsockopt failed");
    close(sock_fd);
    sock_fd = -1;
    return sock_fd;
  }

  result = bind(sock_fd, &server_addr.addr, sizeof(server_addr.addr_in));
  if (result < 0) {
    LOGE("eztune socket bind failed");
    close(sock_fd);
    sock_fd = -1;
    return sock_fd;
  }

  result = listen(sock_fd, 1);
  if (result < 0) {
    LOGE("eztune socket listen failed");
    close(sock_fd);
    sock_fd = -1;
    return sock_fd;
  }

  LOGH("sock_fd: %d, listen at port: %d\n",  sock_fd, port);

  return sock_fd;
}

/** main
 *
 *  Creates the server, and starts waiting for
 *  connections/messages from a prospective
 *  client
 *
 **/
void *eztune_proc(void *data)
{
  int server_socket = -1, client_socket = -1;
  int prev_server_socket = -1, prev_client_socket = -1;

  mm_qcamera_sock_addr_t addr_client_inet;
  socklen_t addr_client_len = sizeof(addr_client_inet.addr_in);
  int result;
  fd_set tsfds;
  int num_fds = 0;
  ssize_t recv_bytes;
  char buf[TUNESERVER_MAX_RECV];

  mm_camera_lib_handle *lib_handle = (mm_camera_lib_handle *)data;

  LOGE(">>> Starting tune server <<< \n");

  // for eztune chromatix params
  server_socket = tunning_server_socket_listen(IP_ADDR, TUNING_CHROMATIX_PORT);
  if (server_socket <= 0) {
    LOGE("[ERR] fail to setup listen socket for eztune chromatix parms...");
    return NULL;
  }
  prev_server_socket = tunning_server_socket_listen(IP_ADDR, TUNING_PREVIEW_PORT);
  if (prev_server_socket <= 0) {
    LOGE("[ERR] fail to setup listen socket for eztune preview...\n");
    return NULL;
  }
  num_fds = TUNESERVER_MAX(server_socket, prev_server_socket);
  LOGH("num_fds = %d\n", num_fds);

  do {
    FD_ZERO(&tsfds);
    FD_SET(server_socket, &tsfds);
    FD_SET(prev_server_socket, &tsfds);
    if (client_socket > 0) {
      FD_SET(client_socket, &tsfds);
    }
    if (prev_client_socket > 0) {
      FD_SET( prev_client_socket, &tsfds);
    }

    /* no timeout */
    result = select(num_fds + 1, &tsfds, NULL, NULL, NULL);
    if (result < 0) {
      LOGE("select failed: %s\n", strerror(errno));
      continue;
    }

    /*
     ** (1) CHROMATIX SERVER
     */
    if (FD_ISSET(server_socket, &tsfds)) {
      LOGD("Receiving New client connection\n");

      client_socket = accept(server_socket,
        &addr_client_inet.addr, &addr_client_len);
      if (client_socket == -1) {
        LOGE("accept failed %s", strerror(errno));
        continue;
      }

      if (client_socket >= FD_SETSIZE) {
        LOGE("client_socket is out of range. client_socket=%d",client_socket);
        continue;
      }

      LOGE("accept a new connect on 55555, sd(%d)\n", client_socket);
      num_fds = TUNESERVER_MAX(num_fds, client_socket);

      // open camera and get handle - this is needed to
      // be able to set parameters without starting
      // preview stream
      /*if (!tsctrl.camera_running) {
        result = tuneserver_open_cam(&tsctrl.lib_handle, &tsctrl);
        if(result) {
          printf("\n Camera Open Fail !!! \n");
          close(server_socket);
          return EXIT_FAILURE;
        }
      }*/
      result = tuneserver_open_cam(lib_handle);
      if(result) {
        LOGE("\n Tuning Library open failed!!!\n");
        close(server_socket);
        return NULL;
      }
      lib_handle->tsctrl.clientsocket_id = client_socket;
      if (tuneserver_ack_onaccept_initprotocol(&lib_handle->tsctrl) < 0) {
        LOGE(" Error while acking\n");
        close(client_socket);
        continue;
      }
      tuneserver_initialize_tuningp(lib_handle, client_socket,
        lib_handle->tsctrl.proto->send_buf, lib_handle->tsctrl.proto->send_len);
    }

    if ((client_socket < FD_SETSIZE) && (FD_ISSET(client_socket, &tsfds))) {
      if (lib_handle->tsctrl.proto == NULL) {
        LOGE(" Cannot receive msg without connect\n");
        continue;
      }

      /*Receive message and process it*/
      recv_bytes = recv(client_socket, (void *)buf,
        lib_handle->tsctrl.proto->next_recv_len, 0);
      LOGD("Receive %lld bytes \n", (long long int) recv_bytes);

      if (recv_bytes == -1) {
        LOGE(" Receive failed with error %s\n",  strerror(errno));
        //tuneserver_check_status(&tsctrl);
        continue;
      } else if (recv_bytes == 0) {
        LOGE("connection has been terminated\n");

        tuneserver_deinitialize_tuningp(&lib_handle->tsctrl, client_socket,
          lib_handle->tsctrl.proto->send_buf,
          lib_handle->tsctrl.proto->send_len);
        free(lib_handle->tsctrl.proto);
        lib_handle->tsctrl.proto = NULL;

        close(client_socket);
        client_socket = -1;
        //tuneserver_check_status(&tsctrl);
      } else {
        LOGD(" Processing socket command\n");

        result = tuneserver_process_client_message(buf, &lib_handle->tsctrl);

        if (result < 0) {
          LOGE("Protocol violated\n");

          free(lib_handle->tsctrl.proto);
          lib_handle->tsctrl.proto = NULL;

          close(client_socket);
          client_socket = -1;
          //tuneserver_check_status(&tsctrl);
          continue;
        }
      }
    }

    /*
     ** (2) PREVIEW SERVER
     */
    if (FD_ISSET(prev_server_socket, &tsfds)) {
      LOGD("Receiving New Preview client connection\n");

      prev_client_socket = accept(prev_server_socket,
        &addr_client_inet.addr, &addr_client_len);
      if (prev_client_socket == -1) {
        LOGE("accept failed %s", strerror(errno));
        continue;
      }
      if (prev_client_socket >= FD_SETSIZE) {
        LOGE("prev_client_socket is out of range. prev_client_socket=%d",prev_client_socket);
        continue;
      }

      lib_handle->tsctrl.pr_clientsocket_id = prev_client_socket;

      LOGD("Accepted a new connection, fd(%d)\n", prev_client_socket);
      num_fds = TUNESERVER_MAX(num_fds, prev_client_socket);

      // start camera
      /*if (!tsctrl.camera_running) {
        result = 0;
        result = tuneserver_open_cam(&tsctrl.lib_handle, &tsctrl);
        if(result) {
          printf("\n Camera Open Fail !!! \n");
          return EXIT_FAILURE;
        }
      }*/
      cam_dimension_t dim;
      //dim.width = lib_handle->test_obj.buffer_width;
      //dim.height = lib_handle->test_obj.buffer_height;
      dim.width = DEFAULT_PREVIEW_WIDTH;
      dim.height = DEFAULT_PREVIEW_HEIGHT;

      LOGD("preview dimension info: w(%d), h(%d)\n", dim.width, dim.height);
      // we have to make sure that camera is running, before init connection,
      // because we need to know the frame size for allocating the memory.
      prevserver_init_protocol(&lib_handle->tsctrl);

      result = tuneserver_initialize_prevtuningp(lib_handle, prev_client_socket,
        dim, (char **)&lib_handle->tsctrl.proto->send_buf,
        &lib_handle->tsctrl.proto->send_len);
      if (result < 0) {
        LOGE("tuneserver_initialize_prevtuningp error!");
        close(prev_client_socket);
        prev_client_socket = -1;
      }
    }

    if ((prev_client_socket < FD_SETSIZE) && (FD_ISSET(prev_client_socket, &tsfds))) {
      recv_bytes = recv(prev_client_socket, (void *)buf,
        lib_handle->tsctrl.pr_proto->next_recv_len, 0);

      LOGD("prev_client_socket=%d\n",  prev_client_socket);
      LOGD("next_recv_len=%d\n",  buf[0]+buf[1]*256);

      if (recv_bytes <= 0) {
        if (recv_bytes == 0) {
          LOGE("client close the connection.\n");
        } else {
          LOGE("receive error: %s\n", strerror(errno));
        }

        //tuneserver_check_status(&tsctrl);
        // if recv error, we should close the connection, free the proto data,
        // AND wait for a new connecton..
        // close_connection();
        // stop_camera()
        // cleanup_proto_data();
        tuneserver_deinitialize_prevtuningp(&lib_handle->tsctrl,
          (char **)&lib_handle->tsctrl.proto->send_buf,
          &lib_handle->tsctrl.proto->send_len);
        close(prev_client_socket);
        prev_client_socket = -1;
      } else {
        result = prevserver_process_client_message((void *)buf,
          &lib_handle->tsctrl);
        if (result < 0) {
          LOGE("Protocol violated\n");

          //free(tsctrl->preivew_proto);
          //free(tsctrl);
          //max_fd = ezt_parms_listen_sd + 1;
          tuneserver_deinitialize_prevtuningp(&lib_handle->tsctrl,
            (char **)&lib_handle->tsctrl.proto->send_buf,
            &lib_handle->tsctrl.proto->send_len);
          close(prev_client_socket);
          prev_client_socket = -1;
          //tuneserver_check_status(&tsctrl);
        }
        //sleep(1);
      }
    }
  } while (1);

  if (server_socket >= 0) {
    close(server_socket);
  }
  if (client_socket >= 0) {
    close(client_socket);
  }
  if (prev_server_socket >= 0) {
    close(prev_server_socket);
  }
  if (prev_client_socket >= 0) {
    close(prev_client_socket);
  }

  return EXIT_SUCCESS;
}

int eztune_server_start (void *lib_handle)
{
  return pthread_create(&eztune_thread_id, NULL,  eztune_proc, lib_handle);
}