#include <gtest/gtest.h>

#include <stdint.h>

extern "C" {
  #include "pa_tz_nwd_task_api.h"
  #include "PaDriverCommand.h"
  #include "PaDriverCommandResponse.h"
}

static PaHandler gHandler;
static const PaTzUserSpaceVirtualAddress kAddress = 0x1000;
static size_t gInputSize = 10;
static int gData[100];
static unsigned gPlatformCallDriverFlag = 0;
static unsigned gPaTzEncoderDriverCommandFlag = 0;
static unsigned gPaTzDecoderDriverCommandResponseFlag = 0;

enum PaTzDecoderDriverCommandResponseResults {
  kFailed = 1,
  kIncorrectReadResponse,
  kReadCommandFailed,
  kWriteCommandFailed,
  kWriteSuccessful
};

extern "C" void PlatformRegisterTrustletInputBuffer(const void *data,
                                                    size_t size) {
}

extern "C" void PlatformRegisterTrustletOutputBuffer(void *data, size_t size) {

}

extern "C" PaTzResult PlatformCallDriver(uint32_t id_driver, uint32_t ioctl_cmd,
                                         void *params, uint32_t size_params) {
  if (gPlatformCallDriverFlag == 1) {
    return PA_TZ_GENERAL_ERROR;
  }

  return PA_TZ_SUCCESS;
}

extern "C" PaTzResult PaEncodeDriverCommand(const PaDriverCommand_t *command,
    void *memory, uint32_t *buffer_size) {

  if (gPaTzEncoderDriverCommandFlag == 1) {
    return PA_TZ_GENERAL_ERROR;
  }

  return PA_TZ_SUCCESS;
}

extern "C" PaTzResult PaDecodeDriverCommandResponse(const void *memory,
    const uint32_t size_buffer, PaDriverCommandResponse_t** command_response) {

  ber_decode(0, &asn_DEF_PaDriverCommandResponse, (void **) command_response,
             memory, size_buffer);

  if (gPaTzDecoderDriverCommandResponseFlag == kFailed) {
    return PA_TZ_GENERAL_ERROR;
  } else if (gPaTzDecoderDriverCommandResponseFlag == kIncorrectReadResponse) {
    (*command_response)->present = PaDriverCommandResponse_PR_NOTHING;
    return PA_TZ_SUCCESS;
  } else if (gPaTzDecoderDriverCommandResponseFlag == kReadCommandFailed) {
    (*command_response)->present = PaDriverCommandResponse_PR_readResponse;
    (*command_response)->choice.readResponse.result = PaDriverCommandResult_paGeneralError;
    return PA_TZ_SUCCESS;
  } else if (gPaTzDecoderDriverCommandResponseFlag == kWriteCommandFailed) {
    (*command_response)->present = PaDriverCommandResponse_PR_result;
    (*command_response)->choice.result = PaDriverCommandResult_paGeneralError;
    return PA_TZ_SUCCESS;
  } else if (gPaTzDecoderDriverCommandResponseFlag == kWriteSuccessful) {
    (*command_response)->present = PaDriverCommandResponse_PR_result;
    return PA_TZ_SUCCESS;
  }

  (*command_response)->present = PaDriverCommandResponse_PR_readResponse;

  return PA_TZ_SUCCESS;
}

class PaTzReadFromNwdTaskTest : public ::testing::Test {
protected:
  virtual void SetUp() {
    // All mocked functions will be return Success
    gPlatformCallDriverFlag = 0;
    gPaTzEncoderDriverCommandFlag = 0;
    gPaTzDecoderDriverCommandResponseFlag = 0;
  }
};

// Test-cases for PaTzReadFromNwdTask() ======================================
TEST_F(PaTzReadFromNwdTaskTest, OutputIsNull_Error) {
  PaTzResult res = PaTzReadFromNwdTask(gHandler, kAddress, gInputSize, NULL);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzReadFromNwdTaskTest, EncodeFailed_Error) {
  gPaTzEncoderDriverCommandFlag = 1;
  PaTzResult res = PaTzReadFromNwdTask(gHandler, kAddress, gInputSize, gData);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzReadFromNwdTaskTest, FailedCallDriver_Error) {
  gPlatformCallDriverFlag = 1;
  PaTzResult res = PaTzReadFromNwdTask(gHandler, kAddress, gInputSize, gData);
  EXPECT_EQ(PA_TZ_DRIVER_COMM_ERROR, res);
}

TEST_F(PaTzReadFromNwdTaskTest, FailedDecodeResponse_Error) {
  gPaTzDecoderDriverCommandResponseFlag = kFailed;
  PaTzResult res = PaTzReadFromNwdTask(gHandler, kAddress, gInputSize, gData);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzReadFromNwdTaskTest, IncorrectResponse_Error) {
  gPaTzDecoderDriverCommandResponseFlag = kIncorrectReadResponse;
  PaTzResult res = PaTzReadFromNwdTask(gHandler, kAddress, gInputSize, gData);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzReadFromNwdTaskTest, FailedReadCommand_Error) {
  gPaTzDecoderDriverCommandResponseFlag = kReadCommandFailed;
  PaTzResult res = PaTzReadFromNwdTask(gHandler, kAddress, gInputSize, gData);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzReadFromNwdTaskTest, Success) {
  PaTzResult res = PaTzReadFromNwdTask(gHandler, kAddress, gInputSize, gData);
  EXPECT_EQ(PA_TZ_SUCCESS, res);
}

// Test-cases for PaTzWriteToNwdTask() ======================================
class PaTzWriteToNwdTaskTest : public ::testing::Test {
protected:
  virtual void SetUp() {
    // All mocked functions will be return Success
    gPlatformCallDriverFlag = 0;
    gPaTzEncoderDriverCommandFlag = 0;
    gPaTzDecoderDriverCommandResponseFlag = 0;
  }
};

TEST_F(PaTzWriteToNwdTaskTest, EncodeFailed_Error) {
  gPaTzEncoderDriverCommandFlag = 1;
  PaTzResult res = PaTzWriteToNwdTask(gHandler, gData, gInputSize, kAddress);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzWriteToNwdTaskTest, FailedCallDriver_Error) {
  gPlatformCallDriverFlag = 1;
  PaTzResult res = PaTzWriteToNwdTask(gHandler, gData, gInputSize, kAddress);
  EXPECT_EQ(PA_TZ_DRIVER_COMM_ERROR, res);
}

TEST_F(PaTzWriteToNwdTaskTest, FailedDecodeResponse_Error) {
  gPaTzDecoderDriverCommandResponseFlag = kFailed;
  PaTzResult res = PaTzWriteToNwdTask(gHandler, gData, gInputSize, kAddress);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzWriteToNwdTaskTest, IncorrectResponse_Error) {
  gPaTzDecoderDriverCommandResponseFlag = kIncorrectReadResponse;
  PaTzResult res = PaTzWriteToNwdTask(gHandler, gData, gInputSize, kAddress);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzWriteToNwdTaskTest, FailedReadCommand_Error) {
  gPaTzDecoderDriverCommandResponseFlag = kWriteCommandFailed;
  PaTzResult res = PaTzWriteToNwdTask(gHandler, gData, gInputSize, kAddress);
  EXPECT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzWriteToNwdTaskTest, Success) {
  gPaTzDecoderDriverCommandResponseFlag = kWriteSuccessful;
  PaTzResult res = PaTzWriteToNwdTask(gHandler, gData, gInputSize, kAddress);
  EXPECT_EQ(PA_TZ_SUCCESS, res);
}
