/**
 * @file socket_utils.c
 * @brief Utility functionality for reading/writing commands to/from socket
 * @author Iaroslav Makarchuk (i.makarchuk@samsung.com)
 * @date Created Oct 3, 2016
 * @par In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include <socket_utils.h>
#include <sys/select.h>

#include <protocol.h>
#include <string.h>
#include <unistd.h>

#define SOCKET_RETRY_CNT    5
#define SOCKET_RETRY_DELAY  1

SocketStatus ReadDataFromSocket(int sock, uint8_t *data, uint32_t data_len) {
  SocketStatus status = SocketStatusError;
  uint32_t curr_len = 0;
  int read_res = 0;

  do {
    read_res = read(sock, data + curr_len, data_len - curr_len);
    if (read_res <= 0) {
      break;
    }

    curr_len += read_res;

  } while (curr_len < data_len);

  if (curr_len == data_len) {
    status = SocketStatusOk;
  }

  return status;
}

SocketStatus WriteDataToSocket(int sock, uint8_t *data, uint32_t data_len) {
  SocketStatus status = SocketStatusError;
  uint32_t curr_len = 0;
  int write_res = 0;

  while (curr_len < data_len) {
    write_res = write(sock, data + curr_len, data_len - curr_len);

    if (write_res <= 0) {
      break;
    }

    curr_len += write_res;
  }

  if (curr_len == data_len) {
    status = SocketStatusOk;
  }

  return status;
}


SocketStatus ReadCommand(int sock, ProtocolCmd *command) {
  return ReadDataFromSocket(sock, (uint8_t *)command, sizeof(ProtocolCmd));
}

SocketStatus WriteCommand(int sock, ProtocolCmd *command) {
  return WriteDataToSocket(sock, (uint8_t *)command, sizeof(ProtocolCmd));
}

SocketStatus ReadPersObj(int sock, PersObjectCmd *po) {
  return ReadDataFromSocket(sock, (uint8_t *)po, sizeof(PersObjectCmd));
}

SocketStatus WritePersObj(int sock, PersObjectCmd *po) {
  return WriteDataToSocket(sock, (uint8_t *)po, sizeof(PersObjectCmd));
}

SocketStatus ReadCancelData(int sock, PersObjectCmd *cancel_data) {
  return ReadDataFromSocket(sock, (uint8_t *)cancel_data, sizeof(PersObjectCmd));
}

SocketStatus WriteCancelData(int sock, PersObjectCmd *cancel_data) {
  return WriteDataToSocket(sock, (uint8_t *)cancel_data, sizeof(PersObjectCmd));
}

SocketStatus ReadTimeData(int sock, PersObjectCmd *time_data) {
  return ReadDataFromSocket(sock, (uint8_t *)time_data, sizeof(PersObjectCmd));
}

SocketStatus WriteTimeData(int sock, PersObjectCmd *time_data) {
  return WriteDataToSocket(sock, (uint8_t *)time_data, sizeof(PersObjectCmd));
}

SocketStatus OpenSocket(struct sockaddr_un *server, int *sock_) {

  int retry_cnt = SOCKET_RETRY_CNT;
  SocketStatus status = SocketStatusError;

  if (!sock_ || !server) {
    goto exit;
  }

  *sock_ = socket(AF_UNIX, SOCK_STREAM, 0);

  if (*sock_ < 0) {
    goto exit;
  }

  while (retry_cnt--) {
    if (!connect(*sock_, (struct sockaddr *)server,
                 sizeof(struct sockaddr_un))) {
      status = SocketStatusOk;
      break;
    }
    sleep(SOCKET_RETRY_DELAY);
  }

  if (SocketStatusOk != status) {
    close(*sock_);
    *sock_ = -1;
  }

exit:
  return status;
}

int CheckSocketInput(int sock_, int msec) {
  fd_set fds;
  struct timeval timeout = { 0 };
  int ret = 0;

  if (sock_ < 0) {
    return 0;
  }
  FD_ZERO(&fds);
  FD_SET(sock_, &fds);

  timeout.tv_sec = 0;
  timeout.tv_usec = msec;

  ret = select(sock_ + 1, &fds, NULL, NULL, &timeout);

  return(ret > 0 && FD_ISSET(sock_, &fds));
}

