#ifndef TESTS_FUCTIONAL_PA_DRIVER_SRC_BASE_COMMAND_H_
#define TESTS_FUCTIONAL_PA_DRIVER_SRC_BASE_COMMAND_H_

extern "C" {
  #include <tee_client_api.h>
  #include "pa_api.h"
  #include "command.h"
}

#include <gtest/gtest.h>
#include <sys/mman.h>

class BaseCommandTest : public ::testing::Test {
 public:
  BaseCommandTest() {
    memset(&handler, 0, sizeof(handler));
  }
  virtual ~BaseCommandTest() {
  }

  static const TEEC_UUID kClientTaUuid;

  virtual void LoadApp();
  virtual void UnloadApp();
  TEEC_Result SendCommand(TciCommand& command);

 protected:

  virtual void SetUp() {
    LoadApp();
  }
  virtual void TearDown() {
    UnloadApp();
  }

  TEEC_Context context;
  TEEC_Session session;
  PaHandler handler;
};

/**
 * Protection flags are placed in the last 3 bits.
 * Iterate over all protectection variants is range from 0 to 7.
 */
static const size_t kFlagsStart = PROT_NONE;
static const size_t kFlagsEnd = (PROT_READ | PROT_WRITE | PROT_EXEC) + 1;

class AccessNwdTask : public BaseCommandTest {
 public:
  AccessNwdTask()
      : secret_buffer("") {
  }
  virtual ~AccessNwdTask() {
  }

 protected:

  virtual void SetUp() {
    BaseCommandTest::SetUp();

    secret_buffer = "a_very_secret_buffer_with_a_key";
    memset(big_buffer, kStartInitValue, sizeof(big_buffer));
    mlock(big_buffer, sizeof(big_buffer));
    mlock((const uint8_t *) secret_buffer.c_str(), secret_buffer.length() + 1);

    for (size_t i = kFlagsStart; i < kFlagsEnd; ++i) {
      void *p = memalign(PAGE_SIZE, kSizeBuffer);
      mlock(p, kSizeBuffer);
      mprotect(p, kSizeBuffer, i);
      // Write to memory something to "real" allocate memory
      if (i & PROT_WRITE) {
        *(char *)p = 0xDE;
      }
      mprotected_buffers[i] = p;
    }
  }

  virtual void TearDown() {
    BaseCommandTest::TearDown();

    munlock(big_buffer, sizeof(big_buffer));
    munlock((const uint8_t *) secret_buffer.c_str(), secret_buffer.length() + 1);

    for (size_t i = kFlagsStart; i < kFlagsEnd; ++i) {
      void *p = mprotected_buffers[i];
      mprotect(p, kSizeBuffer, PROT_READ | PROT_WRITE);
      munlock(p, kSizeBuffer);
      free(p);
    }
  }

  static uint64_t SimpleChecksum(const uint8_t *buffer, size_t size);

  static const size_t kSizeBigBuffer = 2 * PAGE_SIZE + 128;
  static uint8_t big_buffer[kSizeBigBuffer];

  static const size_t kSizeBuffer = PAGE_SIZE;

  std::string secret_buffer;
  
  static void *mprotected_buffers[kFlagsEnd];
};

typedef struct {
  PaTzResult result;
  std::string comment;
} ExpectedResult;

#endif  // TESTS_FUCTIONAL_PA_DRIVER_SRC_BASE_COMMAND_H_
