/**
 * @file persistent_object.c
 * @brief GP Persistent object implementation for host
 * @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 <tee_internal_api.h>
#include <tee_prop_names.h>
#include <protocol.h>
#include <socket_utils.h>
#include <stdio.h>
#include <unistd.h>
#include <tees_log.h>

extern ProtocolCmd g_cmd_buffer;
PersObjectCmd g_po_cmd_buffer = { 0 };

static TEE_Result DoExchange() {
  TEE_Result res = TEE_ERROR_COMMUNICATION;
  struct sockaddr_un addr = { 0 };
  int sock_ = -1;

  addr.sun_family = AF_UNIX;
  snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%u",
           g_cmd_buffer.service_data);

  if (SocketStatusOk != OpenSocket(&addr, &sock_)) {
    goto exit;
  }

  if (SocketStatusOk != WritePersObj(sock_, &g_po_cmd_buffer)) {
    goto exit;
  }

  if (SocketStatusOk == ReadPersObj(sock_, &g_po_cmd_buffer)) {
    res = TEE_SUCCESS;
  }

exit:
  if (sock_ >= 0) {
    close(sock_);
  }
  return res;
}

static TEE_Result SendSimpleCommand(uint32_t command_id,
                                    const void *id, uint32_t id_len) {

  TEE_Result res = TEE_ERROR_BAD_PARAMETERS;

  if (!id || id_len > (MAX_PERSISTENT_OBJECT_ID_LEN - 1)) {
    goto exit;
  }
  TEE_MemFill(&g_po_cmd_buffer, 0x00, sizeof(g_po_cmd_buffer));

  g_po_cmd_buffer.cmd_id = command_id;
  g_po_cmd_buffer.id_len = id_len;
  TEE_MemMove(g_po_cmd_buffer.id, id, id_len);

  res = DoExchange();
  if (TEE_SUCCESS == res) {
    res = g_po_cmd_buffer.cmd_id;
  }

exit:
  return res;
}


TEE_Result ReadFromPO(const uint8_t *id, uint32_t id_len,
                      uint8_t *data, uint32_t *data_len,
                      MultibuildFsStoreDataType data_type) {
  TEE_Result res = TEE_ERROR_BAD_PARAMETERS;

  if (!data || !data_len || !id ||
      id_len > (MAX_PERSISTENT_OBJECT_ID_LEN - 1)) {
    goto exit;
  }

  TEE_MemFill(&g_po_cmd_buffer, 0x00, sizeof(g_po_cmd_buffer));

  switch (data_type) {
    case MB_FS_STORE_METADATA: {
      g_po_cmd_buffer.cmd_id = PROTOCOL_COMMAND_READMD;
      break;
    }
    case MB_FS_STORE_ATTRIB: {
      g_po_cmd_buffer.cmd_id = PROTOCOL_COMMAND_READATTR;
      break;
    }
    default: {
      g_po_cmd_buffer.cmd_id = PROTOCOL_COMMAND_READF;
    }
    break;
  }

  g_po_cmd_buffer.data_len = *data_len;
  g_po_cmd_buffer.id_len = id_len;
  TEE_MemMove(g_po_cmd_buffer.id, id, id_len);

  res = DoExchange();
  if (TEE_SUCCESS != res) {
    goto exit;
  }

  res = g_po_cmd_buffer.cmd_id;
  if (TEE_SUCCESS != res) {
    goto exit;
  }

  TEE_MemMove(data, g_po_cmd_buffer.data, g_po_cmd_buffer.data_len);
  *data_len = g_po_cmd_buffer.data_len;

exit:
  return res;
}

TEE_Result WriteToPO(const uint8_t *id, uint32_t id_len,
                     const uint8_t *data, uint32_t data_len,
                     MultibuildFsStoreDataType data_type) {
  TEE_Result res = TEE_ERROR_BAD_PARAMETERS;

  if (!data || !id || id_len > (MAX_PERSISTENT_OBJECT_ID_LEN - 1)) {
    goto exit;
  }

  TEE_MemFill(&g_po_cmd_buffer, 0x00, sizeof(g_po_cmd_buffer));
  switch (data_type) {
    case MB_FS_STORE_METADATA: {
      g_po_cmd_buffer.cmd_id = PROTOCOL_COMMAND_WRITEMD;
      break;
    }
    case MB_FS_STORE_ATTRIB: {
      g_po_cmd_buffer.cmd_id = PROTOCOL_COMMAND_WRITEATTR;
      break;
    }
    default: {
      g_po_cmd_buffer.cmd_id = PROTOCOL_COMMAND_WRITEF;
    }
    break;
  }
  TEE_MemMove(g_po_cmd_buffer.data, data, data_len);
  g_po_cmd_buffer.data_len = data_len;
  g_po_cmd_buffer.id_len = id_len;
  TEE_MemMove(g_po_cmd_buffer.id, id, id_len);

  res = DoExchange();
  if (TEE_SUCCESS == res) {
    res = g_po_cmd_buffer.cmd_id;
  }

exit:
  return res;


}

TEE_Result RemovePO(const uint8_t *id, uint32_t id_len) {
  return SendSimpleCommand(PROTOCOL_COMMAND_DELETE, id, id_len);
}

TEE_Result StartObjectUpdate(const void *id, uint32_t id_len) {
  return SendSimpleCommand(PROTOCOL_COMMAND_INIT_UPDATE, id, id_len);
}

TEE_Result CommitObjectUpdate(const void *id, uint32_t id_len) {
  return SendSimpleCommand(PROTOCOL_COMMAND_COMMIT_UPDATE, id, id_len);
}

void AbortObjectUpdate(const void *id, uint32_t id_len) {
  if (TEE_SUCCESS !=
      SendSimpleCommand(PROTOCOL_COMMAND_ABORT_UPDATE, id, id_len)) {
    MB_LOGE("Object update abortion failed\n");
  }
}

TEE_Result ListObjects(uint8_t *data, uint32_t *data_len) {
  TEE_Result res = TEE_ERROR_BAD_PARAMETERS;

  if (!data || !data_len) {
    goto exit;
  }

  TEE_MemFill(&g_po_cmd_buffer, 0x00, sizeof(g_po_cmd_buffer));

  g_po_cmd_buffer.cmd_id = PROTOCOL_COMMAND_LIST;
  g_po_cmd_buffer.data_len = *data_len;

  res = DoExchange();
  if (TEE_SUCCESS != res) {
    goto exit;
  }
  res = g_po_cmd_buffer.cmd_id;
  if (TEE_SUCCESS != res) {
    goto exit;
  }
  TEE_MemMove(data, g_po_cmd_buffer.data, g_po_cmd_buffer.data_len);
  *data_len = g_po_cmd_buffer.data_len;

exit:
  return res;
}
