// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Copyright (C) 2015 Red Hat, Inc.
 * Contributor : Matt Benjamin <mbenjamin@redhat.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 * -------------
 */

#include <sys/types.h>
#include <iostream>
#include <vector>
#include <map>
#include <chrono>
#include <thread>
#include <random>
#include "gtest/gtest.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/program_options.hpp>

extern "C" {
/* Don't include rpcent.h; it has C++ issues, and is unneeded */
#define _RPC_RPCENT_H
/* Ganesha headers */
#include "nfs_lib.h"
#include "export_mgr.h"
#include "nfs_exports.h"
#include "sal_data.h"
#include "fsal.h"
}

namespace bf = boost::filesystem;

namespace {

  char* ganesha_conf = nullptr;
  char* lpath = nullptr;
  int dlevel = -1;
  uint16_t export_id = 77;

  struct req_op_context req_ctx;
  struct user_cred user_credentials;
  struct attrlist object_attributes;

  struct gsh_export* a_export = nullptr;
  struct fsal_obj_handle *root_entry = nullptr;
  struct fsal_obj_handle *test_root = nullptr;

#if 0
  std::uniform_int_distribution<uint8_t> uint_dist;
  std::mt19937 rng;

  p->cksum = XXH64(p->data, 65536, 8675309);
#endif

  int ganesha_server() {
    /* XXX */
    return nfs_libmain(
      ganesha_conf,
      lpath,
      dlevel
      );
  }

} /* namespace */

TEST(CI_HASH_DIST1, INIT)
{
  fsal_status_t status;

  a_export = get_gsh_export(export_id);
  ASSERT_NE(a_export, nullptr);

  status = nfs_export_get_root_entry(a_export, &root_entry);
  ASSERT_NE(root_entry, nullptr);

  /* Ganesha call paths need real or forged context info */
  init_op_context_simple(&req_ctx, a_export, a_export->fsal_export);
  memset(&object_attributes, 0, sizeof(object_attributes));
}

TEST(CI_HASH_DIST1, CREATE_ROOT)
{
  fsal_status_t status;
  struct attrlist *attrs_out = nullptr;

  // create root directory for test
  FSAL_SET_MASK(object_attributes.request_mask,
		ATTR_MODE | ATTR_OWNER | ATTR_GROUP);
  object_attributes.mode = 777; /* XXX */
  object_attributes.owner = 667;
  object_attributes.group = 766;

  status = root_entry->obj_ops->mkdir(root_entry, "ci_hash_dist1",
				    &object_attributes, &test_root,
				    attrs_out);
  ASSERT_NE(test_root, nullptr);
}

int main(int argc, char *argv[])
{
  int code = 0;

  using namespace std;
  namespace po = boost::program_options;

  po::options_description opts("program options");
  po::variables_map vm;

  try {

    opts.add_options()
      ("config", po::value<string>(),
	"path to Ganesha conf file")

      ("logfile", po::value<string>(),
	"log to the provided file path")

      ("export", po::value<uint16_t>(),
	"id of export on which to operate (must exist)")

      ("debug", po::value<string>(),
	"ganesha debug level")
      ;

    po::variables_map::iterator vm_iter;
    po::store(po::parse_command_line(argc, argv, opts), vm);
    po::notify(vm);

    // use config vars--leaves them on the stack
    vm_iter = vm.find("config");
    if (vm_iter != vm.end()) {
      ganesha_conf = (char*) vm_iter->second.as<std::string>().c_str();
    }
    vm_iter = vm.find("logfile");
    if (vm_iter != vm.end()) {
      lpath = (char*) vm_iter->second.as<std::string>().c_str();
    }
    vm_iter = vm.find("debug");
    if (vm_iter != vm.end()) {
      dlevel = ReturnLevelAscii(
	(char*) vm_iter->second.as<std::string>().c_str());
    }
    vm_iter = vm.find("export");
    if (vm_iter != vm.end()) {
      export_id = vm_iter->second.as<uint16_t>();
    }

    ::testing::InitGoogleTest(&argc, argv);

    std::thread ganesha(ganesha_server);
    std::this_thread::sleep_for(std::chrono::seconds(5));

    code  = RUN_ALL_TESTS();
    ganesha.join();
  }

  catch(po::error& e) {
    cout << "Error parsing opts " << e.what() << endl;
  }

  catch(...) {
    cout << "Unhandled exception in main()" << endl;
  }

  return code;
}
