#include "arborescentinodelog.h"

static const bool DEBUG = false;

#define aIL arborescentInodeLog

#define WRAP(args...) wrap(this, &aIL::args)

void
aIL::create(str imapfilename, str imapRootMapFilename,
            unsigned long imapCacheSize, str tmapfilename,
            callback<void, ref<inodeLog> >::ref cb, cbe error,
            unsigned long branchT)
{
  imapTree::create(imapfilename, imapRootMapFilename, branchT,
                   imapCacheSize,
                   wrap(create_after_imap, tmapfilename, cb, error), error);
}

void
aIL::create_after_imap(str tmapfilename,
                       callback<void, ref<inodeLog> >::ref cb, cbe error,
                       ref<imapTree> imap)
{
  tmapTree::create(tmapfilename, 1024,
                   wrap(create_after_tmap, imap, cb, error), error);
}

void
aIL::create_after_tmap(ref<imapTree> imap,
                       callback<void, ref<inodeLog> >::ref cb, cbe error,
                       ref<tmapTree> tmap)
{
  ref<aIL> log = New refcounted<aIL>(imap, tmap);

  cb(log);
}

aIL::aIL(ref<imapTree> imap, ref<tmapTree> tmap)
  : imap(imap), tmap(tmap)
{
  ptr<inode> r = imap->syncPredecessor(ULLONG_MAX, imap->getTS());
  if (r == NULL) {
    nextInumber = 1;
  } else {
    nextInumber = r->num+1;
  }
}

aIL::~aIL()
{
}

void
aIL::del(inumber num,
         callback<void, timestamp>::ref cb, cbe error)
{
  fatal << "BUG: arborescentInodeLog doesn't implement del.\n";
}

void
aIL::put(inode node,
         callback<void, timestamp>::ref cb, cbe error)
{
  timestamp realTS = now();
  imap->insert(node.num, node);
  treeTimestamp treeTS = imap->commit();
  tmap->insert(realTS, treeTS);

  if (DEBUG)
    warn << "ail: put inumber " << node.num << " at ts " << realTS
         << " with tree ts " << treeTS << "\n";
  
  cb(realTS);
}

void
aIL::get(timestamp ts, inumber num,
         callback<void, inode>::ref cb, cbe error)
{
  if (DEBUG)
    warn << "ail: get for inumber " << num << " at time " << ts << "\n";
  tmap->predecessor(ts, WRAP(get_after_tmap, num, cb, error), error);
}

void
aIL::get_after_tmap(inumber num,
                    callback<void, inode>::ref cb, cbe error,
                    timestamp realTS, treeTimestamp treeTS)
{
  if (DEBUG)
    warn << "ail: get after tmap found real ts " << realTS
         << " mapped to tree ts " << treeTS << "\n";
  imap->search(num, treeTS,
               WRAP(get_after_imap, num, cb, error), error);
}

void
aIL::get_after_imap(inumber realNum,
                    callback<void, inode>::ref cb, cbe error,
                    inumber num, inode node)
{
  if (DEBUG)
    warn << "ail: get after imap found inumber " << num << "\n";

  if (realNum != num) {
    fatal << "BUG: Lookup for inumber " << realNum
          << " returned inumber " << num << "\n";
  }
  if (num != node.num) {
    fatal << "BUG: Lookup for inumber " << num
          << " found the right inumber, but returned inode with inumber "
          << node.num << "\n";
  }

  cb(node);
}

void
aIL::newInumber(callback<void, inumber>::ref cb, cbe error)
{
  cb(nextInumber++);
}

void
aIL::setRootInumber(inumber num, callback<void>::ref cb, cbe error)
{
  if (num != 1) {
    fatal << "BUG: Lazy implementor didn't implement setting root inumber\n";
  }
  cb();
}

void
aIL::getRootInumber(callback<void, inumber>::ref cb, cbe error)
{
  if (imap->syncSearch(1, imap->getTS()) != NULL) {
    cb(1);
  } else {
    cb(0);  
  }
}

unsigned long
aIL::blockTransfers()
{
  return imap->blockTransfers();
}

void
aIL::resetBlockTransfers()
{
  imap->resetBlockTransfers();
}
