#include <gtest/gtest.h>

#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <cstdio>

extern "C" {
  #include "pa_provision.h"
  #include "nwd_log.h"
}

#include "PaDriverCommand.h"
#include "PaDriverCommandResponse.h"

extern "C" void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
  return (void*) 0x01;
}

extern "C" int munmap(void *addr, size_t length) {
  return 0;
}

PaResult g_handler_create_result = PA_SUCCESS;

extern "C" PaResult PaHandlerCreate(PaHandler *handler) {
  return g_handler_create_result;
}

extern "C" void PaHandlerDestroy(PaHandler *handler) {
}

extern "C" int HidlNewCertificate(uint8_t *buffer, size_t *buffer_size) {
  return 0;
}

extern "C" void PaFreeDriverCommandResponse(PaDriverCommandResponse_t *command_response) {
}

extern "C" int PaEncodeDriverCommand(const PaDriverCommand_t *command, void *memory,
      uint32_t *size_memory) {
  return 0;
}

extern "C" int PaDecodeDriverCommandResponse(
    const void *memory, const uint32_t size_buffer,
    PaDriverCommandResponse_t** command_response) {
    uint8_t size[] = {0xA};
    *command_response = (PaDriverCommandResponse_t*) malloc(sizeof(PaDriverCommandResponse_t));
    (*command_response)->choice.provisioningResponse.signedCertificate.buf = (uint8_t*) malloc(sizeof(size));
    (*command_response)->present = PaDriverCommandResponse_PR_provisioningResponse;
    (*command_response)->choice.provisioningResponse.result = PaDriverCommandResult_paSuccess;
    memcpy((*command_response)->choice.provisioningResponse.signedCertificate.buf, size, sizeof(size));
    (*command_response)->choice.provisioningResponse.signedCertificate.size = sizeof(size);
  return 0;
}

class PaNewCertificateTest : public ::testing::Test {
public:
  int arbitrary_fd_ = 0xFF;
  int correct_fd_;
  const char file_name_[20] = "test_file";
  const char pack_name_[15] = "com.samsung.pa";
  const uint8_t rsa_key_[10] =  {0x01, 0x02, 0x03};
  const size_t rsa_sz_ = sizeof(rsa_key_);

  void SetUp() {
    g_handler_create_result = PA_SUCCESS;

    int fd = open(file_name_, O_CREAT | O_WRONLY | O_APPEND, 0666);
    ASSERT_GT(fd, 0);

    const char file_content[] = "File created!";
    int w_bytes = write(fd, file_content, sizeof(file_content));
    ASSERT_EQ(sizeof(file_content), w_bytes);
    correct_fd_ = fd;
  }
  void TearDown() {
    remove(file_name_);
  }
};

TEST_F(PaNewCertificateTest, NullFd_Invalid) {
  PaResult res = PA_GENERAL_ERROR;
  res = PaNewCertificate(0, NULL, rsa_key_, rsa_sz_);
  ASSERT_EQ(PA_INVALID_ARGUMENTS , res);
}

TEST_F(PaNewCertificateTest, NullPackageName_Invalid) {
  PaResult res = PA_GENERAL_ERROR;
  res = PaNewCertificate(arbitrary_fd_, NULL, rsa_key_, rsa_sz_);
  EXPECT_EQ(PA_INVALID_ARGUMENTS ,res);
}

TEST_F(PaNewCertificateTest, NullRsa_Invalid) {
  PaResult res = PA_GENERAL_ERROR;
  res = PaNewCertificate(arbitrary_fd_, pack_name_, NULL, rsa_sz_);
  EXPECT_EQ(PA_INVALID_ARGUMENTS ,res);
  }

TEST_F(PaNewCertificateTest, ZeroRsaSize_Invalid) {
  PaResult res = PA_GENERAL_ERROR;
  res = PaNewCertificate(arbitrary_fd_, pack_name_, rsa_key_, 0);
  EXPECT_EQ(PA_INVALID_ARGUMENTS ,res);
}

TEST_F(PaNewCertificateTest, InvalidDescriptor) {
  PaResult res = PA_GENERAL_ERROR;

  res = PaNewCertificate(arbitrary_fd_, pack_name_, rsa_key_, rsa_sz_);
  EXPECT_EQ(PA_GENERAL_ERROR ,res);
}

TEST_F(PaNewCertificateTest, ValidData_Success) {
  PaResult res = PA_GENERAL_ERROR;

  res = PaNewCertificate(correct_fd_, pack_name_, rsa_key_, rsa_sz_);
  EXPECT_EQ(PA_SUCCESS ,res);
}

TEST_F(PaNewCertificateTest, UnsignedApplication_Error) {
  PaResult res = PA_GENERAL_ERROR;

  // Provision application does not have PA ID
  g_handler_create_result = PA_GENERAL_ERROR;

  res = PaNewCertificate(correct_fd_, pack_name_, rsa_key_, rsa_sz_);
  EXPECT_EQ(PA_GENERAL_ERROR ,res);
}
