#include "persifsd.h"
#include "fs.h"
#include "nfsprovider.h"
#include "superblob.h"
#include "inodelog.h"
#include <nfsserv.h>
#include <classfscli.h>
#include <unistd.h>

static ref<nfsserv_udp> nfss_udp = New refcounted<nfsserv_udp>;
static ref<nfsserv_fixup> nfss = New refcounted<nfsserv_fixup> (nfss_udp);

static void
usage(const char *name)
{
  warn << "Usage: " << name
       << " [-a] [-d disk] [-g secs] [-c size] mountdir\n";
  warn << "\n";
  warn << "  -a       "
       << "Use arborescent inode log instead of logging inode log\n";
  warn << "  -d disk  "
       << "Specify name of directory to use for disk storage\n";
  warn << "  -g secs  "
       << "Specify write grouping delay.  -g 0 disables write grouping\n";
  warn << "  -c size  "
       << "Specify imap cache size in blocks.\n";
  exit(1);
}

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

  bool arborescent = false;
  str diskName("disk");
  int writeGrouping = 5;
  unsigned long cacheSize = 128;
  
  while (true) {
    int c = getopt(argc, argv, "ad:g:");
    char *end;
    if (c == -1)
      break;

    switch (c) {
    case 'a':
      arborescent = true;
      break;

    case 'd':
      diskName = str(optarg);
      break;

    case 'g':
      writeGrouping = strtol(optarg, &end, 10);
      if (*end != '\0') {
        warn << "Non-number " << optarg << " specified for -g\n";
        usage(argv[0]);
      }
      break;

    case 'c':
      cacheSize = strtol(optarg, &end, 10);
      if (*end != '\0') {
        warn << "Non-number " << optarg << " specified for -c\n";
        usage(argv[0]);
      }
      break;

    default:
      fatal << "BUG: getopt returned " << c;
    }
  }

  if (optind != argc-1) {
    warn << "Must specify mount name\n";
    usage(argv[0]);
  }

  str mountName(argv[optind]);

  fsCreateArgs args;
  args.filebase = diskName;
  args.arborescent = arborescent;
  args.writeGrouping = writeGrouping;
  args.imapCacheSize = cacheSize;
  delaycb(0, wrap(persifsd::startTheWorld,
                  mountName, args));

  amain ();
}

void
persifsd::startTheWorld(str mountName, fsCreateArgs args)
{
  str diskName = args.filebase;
  
  if (access(diskName.cstr(), F_OK) != 0)
    if (mkdir(diskName.cstr(), 0755))
      fatal << "Failed to create disk directory " << diskName << "\n";

  strbuf buf(diskName);
  buf << "/persifs";
  args.filebase = str(buf);
  
  FS::create(args,
             wrap(after_fsCreate, mountName), fatalCB);
}

void
persifsd::after_fsCreate(str mountName, ref<FS> fs)
{
  nfsProvider::create(fs, wrap(after_nfsCreate, mountName), fatalCB);
}

void
persifsd::after_nfsCreate(str mountName, ref<nfsProvider> nfs)
{
  nfss->setcb(wrap(nfs, &nfsProvider::dispatch));
  classmount(nfss_udp->getfd(), nfs->getRootFH().data, mountName,
             wrap(after_mount, mountName));
}

void
persifsd::after_mount(str mountName, int err)
{
  if(err == 0){
    warn << "PersiFS mounted as " << mountName << "\n";
  } else {
    fatal << "classfsd error: " << strerror(err) << "\n";
  }
}
