#include <gtest/gtest.h>
#include <stdint.h>

extern "C" {
  #include "pa_tz_api.h"
  #include "serialize.h"
}

static PaHandler gValidHandler;
static uint8_t gValidCommandBuffer[10];
static PaTzResult gMockCallDriverResult = PA_TZ_SUCCESS;
static uint32_t gMockCallDriverCounter = 0;
static uint32_t gMocIsAuthenticateSuccessCounter = 0;
static const char kValidProcessId[] = "valid_process_id";
static const int kValidPid = 1459;
static const uint32_t kIncorrectProcessNameLength = 1050;

static const PaDriverCommandResponse_t *GetValidAuthenticationResponse() {
  static PaDriverCommandResponse_t resp;
  resp.present = PaDriverCommandResponse_PR_authenticateResponse;
  resp.choice.authenticateResponse.result = PaDriverAuthenticateResult_paAuthenticated;
  OCTET_STRING_fromString(&resp.choice.authenticateResponse.processInfo,
                          kValidProcessId);

  return &resp;
}

extern "C" void PlatformRegisterTrustletInputBuffer(const void *data, size_t size) {
}

extern "C" PaTzResult PlatformCallDriver(uint32_t id_driver, uint32_t ioctl_cmd,
                                         const void *params,
                                         uint32_t size_params) {
  PaEncodeDriverCommandResponse(GetValidAuthenticationResponse(),
                                   (void *) params, &size_params);
  gMockCallDriverCounter++;

  return gMockCallDriverResult;
}

extern "C" PaTzResult ConvertAuthenticateResult(
    const PaDriverCommandAuthenticate_t *command_authenticate) {
  gMocIsAuthenticateSuccessCounter++;

  return gMockCallDriverResult;
}

class PaTzAuthenticateTest : public ::testing::Test {
protected:
  virtual void SetUp() {
      mRules = PaTzRulesCreate();
      gMockCallDriverResult = PA_TZ_SUCCESS;
      gMockCallDriverCounter = 0;
      gMocIsAuthenticateSuccessCounter = 0;
  }

  virtual void TearDown() {
      PaTzRulesDestroy(mRules);
      PaTzHandlerDestroy(&m_handler);
  }

  PaTzRules mRules;
  PaHandler m_handler;
};

TEST_F(PaTzAuthenticateTest, PaTzAuthenticate_Valid) {
  PaTzResult res = PaTzAuthenticate(gValidHandler);
  EXPECT_TRUE(PA_TZ_SUCCESS == res);
}

TEST_F(PaTzAuthenticateTest, PaTzAuthenticateWithCommandBuffer_Valid) {
  PaTzResult res = PaTzAuthenticateWithCommandBuffer(gValidHandler,
                                                     gValidCommandBuffer,
                                                     sizeof(gValidCommandBuffer),
                                                     NULL);
  EXPECT_TRUE(PA_TZ_SUCCESS == res);
}

TEST_F(PaTzAuthenticateTest, PaTzAuthenticateWithRules_DriverIsCalled) {
  static uint8_t gValidRulesBuffer[4096];
  PaTzResult res = PaTzAuthenticateWithRules(gValidHandler,
                                             gValidRulesBuffer, NULL);
  EXPECT_TRUE(PA_TZ_SUCCESS == res);
  EXPECT_TRUE(gMockCallDriverCounter > 0);
  EXPECT_TRUE(gMocIsAuthenticateSuccessCounter > 0);
}

TEST_F(PaTzAuthenticateTest, PaTzAuthenticateWithRules_NullRules) {
  PaTzResult res = PaTzAuthenticateWithRules(gValidHandler, NULL, NULL);
  EXPECT_TRUE(PA_TZ_SUCCESS == res);
}

TEST_F(PaTzAuthenticateTest, WithRulesAndInternalAppName_Failed) {
  PaTzResult res = PaTzHandlerCreateFromProcessName(kValidProcessId, &m_handler);
  EXPECT_EQ(PA_TZ_SUCCESS, res);

  res = PaTzRulesAddProcessName(mRules, kValidProcessId);
  EXPECT_EQ(PA_TZ_SUCCESS, res);
  
  res = PaTzAuthenticateWithRules(m_handler, mRules, NULL);
  EXPECT_EQ(PA_TZ_INCOMPATIBLE_RULES, res);
}

TEST_F(PaTzAuthenticateTest, WithInternalAppName_Success) {
  PaTzResult res = PaTzHandlerCreateFromProcessName(kValidProcessId, &m_handler);
  EXPECT_EQ(PA_TZ_SUCCESS, res);

  res = PaTzAuthenticate(m_handler);
  EXPECT_EQ(PA_TZ_SUCCESS, res);
}

TEST_F(PaTzAuthenticateTest, WithInternalAppNameAndEmptyRules_Success) {
  PaTzResult res = PaTzHandlerCreateFromProcessName(kValidProcessId, &m_handler);
  EXPECT_EQ(PA_TZ_SUCCESS, res);

  res = PaTzAuthenticateWithRules(m_handler, mRules, NULL);
  EXPECT_EQ(PA_TZ_SUCCESS, res);
}

TEST_F(PaTzAuthenticateTest, WithInternalPid_Success) {
  PaTzResult res = PaTzHandlerCreateFromPid(kValidPid, &m_handler);
  EXPECT_EQ(PA_TZ_SUCCESS, res);

  res = PaTzAuthenticate(m_handler);
  EXPECT_EQ(PA_TZ_SUCCESS, res);
}

class PaTzHandlerTest : public ::testing::Test {
protected:
  virtual void SetUp() {
    gMockCallDriverResult = PA_TZ_SUCCESS;
    gMockCallDriverCounter = 0;
    gMocIsAuthenticateSuccessCounter = 0;
  }

  virtual void TearDown() {
    PaTzHandlerDestroy(&m_handler);
  }

  PaHandler m_handler;
};

TEST_F(PaTzHandlerTest, CreateFromPidWithNull_Failed) {
  PaTzResult res = PaTzHandlerCreateFromPid(kValidPid, NULL);
  ASSERT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzHandlerTest, CreateFromPidValid_Success) {
  PaTzResult res = PaTzHandlerCreateFromPid(kValidPid, &m_handler);
  EXPECT_EQ(PA_TZ_SUCCESS, res);
  EXPECT_EQ(m_handler.pid, kValidPid);
}

TEST_F(PaTzHandlerTest, CreateFromProcessNameWithNullHandler_Failed) {
  PaTzResult res = PaTzHandlerCreateFromProcessName(kValidProcessId, NULL);
  ASSERT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzHandlerTest, CreateFromProcessNameWithNullName_Failed) {
  PaTzResult res = PaTzHandlerCreateFromProcessName(NULL, &m_handler);
  ASSERT_EQ(PA_TZ_GENERAL_ERROR, res);
}

TEST_F(PaTzHandlerTest, CreateFromProcessNameWithHugeName_Failed) {
  static char huge_process_name[kIncorrectProcessNameLength] = "";
  for (int i = 0; i < kIncorrectProcessNameLength; i++) {
    huge_process_name[i] = 'x';
  }

  PaTzResult res = PaTzHandlerCreateFromProcessName(huge_process_name, &m_handler);
  ASSERT_EQ(PA_TZ_AF_APPNAME_IS_INCORRECT, res);
}

TEST_F(PaTzHandlerTest, CreateFromProcessNameValid_Success) {
  PaTzResult res = PaTzHandlerCreateFromProcessName(kValidProcessId, &m_handler);
  ASSERT_EQ(PA_TZ_SUCCESS, res);
}
