#include <gtest/gtest.h>

using namespace std;

extern "C" {
    #include "serialize.h"
    #include "PaDriverCommand.h"
}


static int EncoderDriverCommandResponse(
    const PaDriverCommandResponse_t *command_response, void *memory, uint32_t buffer_size) {

  // Encode the command type as DER(BER)
  asn_enc_rval_t result_encode = der_encode_to_buffer(
      &asn_DEF_PaDriverCommandResponse, (void *)command_response, memory, buffer_size);
  return result_encode.encoded;
}


class Serialize : public ::testing::Test {
 public:
  PaDriverCommand_t command_;
  uint8_t good_in_buffer_[4096];
  uint8_t bad_in_buffer_[10];

  uint32_t good_in_buffer_size_;
  uint32_t bad_in_buffer_size_;

  uint8_t encoded_response_[1024];
  PaDriverCommandResponse_t *response_;
  PaDriverCommandResponse_t good_response_;

  PaCertificate_t *certificate_;

  PaDriverCommand_t driver_command_;
  PaDriverCommandAuthenticate_t *command_authenticate_;

  void SetUp() {
    good_in_buffer_size_ = sizeof(good_in_buffer_);
    bad_in_buffer_size_ = sizeof(bad_in_buffer_);
    response_ = NULL;
    certificate_ = NULL;

    memset(&driver_command_, 0, sizeof(driver_command_));
    driver_command_.present = PaDriverCommand_PR_authenticateCommand;
    command_authenticate_ = &driver_command_.choice.authenticateCommand;

    good_response_.present = PaDriverCommandResponse_PR_result;
    good_response_.choice.result = PaDriverCommandResult_paSuccess;

    ASSERT_GT(EncoderDriverCommandResponse(&good_response_, encoded_response_, sizeof(encoded_response_)), 0);
  }

  void TearDown() {
    if (response_) {
      PaFreeDriverCommandResponse(response_);
    }
    if (certificate_) {
      PaFreeCertificate(certificate_);
    }
  }
};

TEST_F(Serialize, PaEncodeDriverCommand_WrongInputParams) {
  ASSERT_EQ(-1, PaEncodeDriverCommand(NULL, (void *)&good_in_buffer_, &good_in_buffer_size_));
  ASSERT_EQ(-1, PaEncodeDriverCommand(&command_, (void *)good_in_buffer_, NULL));
}

TEST_F(Serialize, PaEncodeDriverCommand_Success) {
  ASSERT_EQ(0, PaEncodeDriverCommand(&driver_command_, (void *)&good_in_buffer_, &good_in_buffer_size_));
}

TEST_F(Serialize, PaEncodeDriverCommand_Fail) {
  ASSERT_EQ(-1, PaEncodeDriverCommand(&driver_command_, (void *)&bad_in_buffer_, &bad_in_buffer_size_));
}

TEST_F(Serialize, PaDecodeDriverCommandResponse_ResponseIsNull) {
  ASSERT_EQ(-1, PaDecodeDriverCommandResponse(encoded_response_, sizeof(encoded_response_), NULL));
}

TEST_F(Serialize, PaDecodeDriverCommandResponse_MemoryIsNull) {
  ASSERT_EQ(-1, PaDecodeDriverCommandResponse(NULL, sizeof(encoded_response_), &response_));
}

TEST_F(Serialize, PaDecodeDriverCommandResponse_MemoryIsSmall) {
  ASSERT_EQ(-1, PaDecodeDriverCommandResponse(encoded_response_, 1, &response_));
}

TEST_F(Serialize, PaDecodeDriverCommandResponse_Success) {
  ASSERT_EQ(0, PaDecodeDriverCommandResponse(encoded_response_, sizeof(encoded_response_), &response_));
}

TEST_F(Serialize, PaFreeDriverCommandResponse_ResponseIsNull) {
  EXPECT_NO_FATAL_FAILURE(PaFreeDriverCommandResponse(NULL));
}

TEST_F(Serialize, PaDecodeCertificate_CertificateIsNull) {
  ASSERT_EQ(-1, PaDecodeCertificate(encoded_response_, sizeof(encoded_response_), NULL));
}

TEST_F(Serialize, PaDecodeCertificate_MemoryIsNull) {
  ASSERT_EQ(-1, PaDecodeCertificate(NULL, sizeof(encoded_response_), &certificate_));
}

TEST_F(Serialize, PaDecodeCertificate_MemoryIsSmall) {
  ASSERT_EQ(-1, PaDecodeCertificate(encoded_response_, 1, &certificate_));
}

TEST_F(Serialize, PaFreeCertificate_CertificateIsNull) {
  EXPECT_NO_FATAL_FAILURE(PaFreeCertificate(NULL));
}

static PaDriverCommand_t g_command;

TEST_F(Serialize, PaTzEncoderDriverCommandTest_BufferNull) {
  int result;
  uint32_t *buffer_null = NULL;
  uint32_t buffer_size = 0;

  result = PaEncodeDriverCommand(&g_command, buffer_null, &buffer_size);

  EXPECT_EQ(-1, result) <<
      "Encoding SHOULD fail if output buffer is NULL";
}

TEST_F(Serialize, PaTzEncoderDriverCommandTest_BufferSizeNull) {
  int result;
  uint32_t buffer[1];

  result = PaEncodeDriverCommand(&g_command, &buffer[0], NULL);

  EXPECT_EQ(-1, result) <<
      "Encoding SHOULD fail if output buffer is NULL";
}