/*
 * Copyright (c) 2018-2019,2021 Qualcomm Technologies, Inc.
 * All Rights Reserved.
 * Confidential and Proprietary - Qualcomm Technologies, Inc.
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "example_app.h"
#include "example_app_methods.h"
#include "hash.h"
#include "hash_init.h"
#include "BufferAllocatorWrapperSim.h"
#include "run_hash.h"
#include "stringl.h"
#include "utils.h"

#define ION_BUFFER_ALIGNMENT 0x1000

int run_qsee_hash(struct QSEECom_handle *app_handle)
{
  int error;

  struct example_app_req req;
  struct example_app_rsp rsp;

  struct hash_req hash_req;
  struct hash_rsp hash_rsp;

  struct hash_rsp *hash_result;

  /* This input could be gathered from the user in some way. */
  uint32_t alg = HASH_SHA512;

  const char msg[] = "mycustommessage";

  hash_req_init(&hash_req, QSEE_HASH, alg, (const uint8_t *)msg, strlen(msg));

  example_app_req_hash_init(&req, &hash_req);
  example_app_rsp_hash_init(&rsp, &hash_rsp);

  printf("Hashing msg '%s' with qsee_hash:\n", msg);

  error = QSEECom_send_cmd(app_handle, &req, sizeof(req), &rsp, sizeof(rsp));

  if (error == QSEECOM_SUCCESS) {
    hash_result = example_app_rsp_get_rsp_struct(&rsp);
    if (!hash_result) {
      error = HASH_FAILURE;
      goto out;
    }
    if (hash_result->status == HASH_SUCCESS) {
      printf("Hashed msg:\n");
      print_hex(hash_result->hash_result_buffer, hash_result->hash_size);
    } else {
      error = HASH_FAILURE;
      printf("Hashing failed - error: %d\n", hash_result->status);
    }
  }

out:
  return error ? HASH_FAILURE : HASH_SUCCESS;
}

int run_qsee_hash_modified_cmd(struct QSEECom_handle *app_handle)
{
  int error;

  struct QSEECom_ion_fd_info ion_fd_info;

  struct example_app_req req;
  struct example_app_rsp rsp;

  struct hash_req hash_req;
  struct hash_rsp hash_rsp;

  struct hash_rsp *hash_result;

  /* This input could be gathered from the user in some way. */
  uint32_t alg = HASH_SHA256;

  const char msg[] = "message";

  BufferAllocatorSimHandle *msg_ion_handle = NULL;
  BufferAllocatorSimHandle *hash_result_ion_handle = NULL;

  msg_ion_handle = CreateDmabufHeapBufferAllocatorSim();
  if(!msg_ion_handle) {
    return -1;
  }

  hash_result_ion_handle = CreateDmabufHeapBufferAllocatorSim();
  if(!hash_result_ion_handle) {
    return -1;
  }

  int msg_fd = DmabufHeapAllocSim(msg_ion_handle, sizeof(msg), ION_BUFFER_ALIGNMENT);
  if (msg_fd < 0) {
    return -1;
  }

  int hash_fd = DmabufHeapAllocSim(hash_result_ion_handle, HASH_MAX_HASH_SIZE, ION_BUFFER_ALIGNMENT);
  if (hash_fd < 0) {
    freeSim(msg_fd);
    DmabufHeapBufferFreeSim(msg_ion_handle);
    return -1;
  }

  const size_t message_len = strlcpy(mapGetBuffer(msg_ion_handle), msg, mapGetBufferSize(msg_ion_handle));
  if (message_len >= mapGetBufferSize(msg_ion_handle)) {
    printf("Warning: Message was truncated to fit in ion buffer.\n");
  }

  /* Message ptr will be filled by QSEECom_send_modified_cmd. */
  hash_req_init(&hash_req, QSEE_HASH_MODIFIED_CMD, alg, NULL, strlen(msg));

  example_app_req_hash_init(&req, &hash_req);
  example_app_rsp_hash_init(&rsp, &hash_rsp);

  memset(&ion_fd_info, 0, sizeof(ion_fd_info));

  ion_fd_info.data[0].fd = msg_fd;
  ion_fd_info.data[0].cmd_buf_offset =
    (uintptr_t)&req.cmd_req.hash_req.msg_ptr - (uintptr_t)&req;

  ion_fd_info.data[1].fd = hash_fd;
  ion_fd_info.data[1].cmd_buf_offset =
    (uintptr_t)&req.cmd_req.hash_req.hash_result_ptr - (uintptr_t)&req;

  printf("Hashing msg '%s' with qsee_hash (via send_modified_cmd):\n", msg);

  if (sizeof(void *) == 8) {
    error = QSEECom_send_modified_cmd_64(app_handle,
                                         &req,
                                         sizeof(req),
                                         &rsp,
                                         sizeof(rsp),
                                         &ion_fd_info);
  } else {
    error = QSEECom_send_modified_cmd(app_handle,
                                      &req,
                                      sizeof(req),
                                      &rsp,
                                      sizeof(rsp),
                                      &ion_fd_info);
  }

  if (error == QSEECOM_SUCCESS) {
    hash_result = example_app_rsp_get_rsp_struct(&rsp);
    if (!hash_result) {
      error = HASH_FAILURE;
      goto out;
    }
    if (hash_result->status == HASH_SUCCESS) {
      printf("Hashed msg:\n");
      print_hex(mapGetBuffer(hash_result_ion_handle), hash_result->hash_size);
    } else {
      error = HASH_FAILURE;
      printf("Hashing failed - error: %d\n", hash_result->status);
    }
  }

out:
  freeSim(msg_fd);
  freeSim(hash_fd);
  DmabufHeapBufferFreeSim(msg_ion_handle);
  DmabufHeapBufferFreeSim(hash_result_ion_handle);

  return error ? HASH_FAILURE : HASH_SUCCESS;
}

int run_invoke_hash(struct QSEECom_handle *app_handle)
{
  int error;

  struct example_app_req req;
  struct example_app_rsp rsp;

  struct hash_req invoke_hash_req;
  struct hash_rsp invoke_hash_rsp;

  struct hash_rsp *hash_result;

  /* This input could be gathered from the user in some way. */
  uint32_t alg = HASH_SHA384;

  const char msg[] = "anothermessage";

  hash_req_init(&invoke_hash_req, INVOKE_HASH, alg, (const uint8_t *)msg, strlen(msg));

  example_app_req_hash_init(&req, &invoke_hash_req);
  example_app_rsp_hash_init(&rsp, &invoke_hash_rsp);

  printf("Hashing msg '%s' with invoke_hash:\n", msg);

  error = QSEECom_send_cmd(app_handle, &req, sizeof(req), &rsp, sizeof(rsp));

  if (error == QSEECOM_SUCCESS) {
    hash_result = example_app_rsp_get_rsp_struct(&rsp);
    if (!hash_result) {
      error = INVOKE_HASH_FAILURE;
      goto out;
    }
    if (hash_result->status == HASH_SUCCESS) {
      printf("Hashed msg:\n");
      print_hex(hash_result->hash_result_buffer, hash_result->hash_size);
    } else {
      error = INVOKE_HASH_FAILURE;
      printf("Hashing failed - error: %d\n", hash_result->status);
    }
  }

out:
  return error ? INVOKE_HASH_FAILURE : HASH_SUCCESS;
}
