/*
 * Inode log module
 *
 * This module maintains the inode log on disk and provides the
 * interface to the inode map internally.
 */

#ifndef INODELOG_H
#define INODELOG_H

#include "persifs.h"
#include "chunkable.h"
#include "marshal.h"
#include <nfs3_prot.h>
#include <time.h>

typedef unsigned int timestamp;
static const timestamp TIMESTAMP_LATEST = ((timestamp)-1);

typedef unsigned long long inumber;
class inode;

/**
 * Inodes are variable length and include the standard inode file
 * metadata, the inumber, and the marshalling of the chunkable
 * representing the file's content.
 */
class inode : public fattr3
{
public:
  inumber num;
  chunkable::marshalled content;

  void setFromFattr(fattr3 fa);
};

/**
 * Abstract inode log class.  This collects inode information and can
 * be queried for the inode mapping for any inode at any point in
 * time.
 */
class inodeLog
{
public:
  static unsigned long entryCount;
  static unsigned long logSize;

  /**
   * Append a new entry to the inode log.
   */
  virtual
  void put(inode node,
           callback<void, timestamp>::ref cb, cbe error) = 0;

  /**
   * Append an inode deletion to the log.
   */
  virtual
  void del(inumber num,
           callback<void, timestamp>::ref cb, cbe error) = 0;

  /**
   * Load an inode from the inode log, given its timestamp and
   * inumber.  timestamp can be TIMESTAMP_LATEST to load the inode
   * from the current version of the file system.
   */
  virtual
  void get(timestamp ts, inumber num,
           callback<void, inode>::ref cb, cbe error) = 0;

  /**
   * Generate a new, unique inumber.  There are no guarantees on the
   * unguessability of this inumber.
   */
  virtual
  void newInumber(callback<void, inumber>::ref cb, cbe error) = 0;

  /**
   * Set root inumber.  If a root inumber has already been set, this
   * is an error.
   */
  virtual
  void setRootInumber(inumber num, callback<void>::ref cb, cbe error) = 0;

  /**
   * Get root inumber.  Returns 0 if no root inumber has been set.
   */
  virtual
  void getRootInumber(callback<void, inumber>::ref cb, cbe error) = 0;

  /**
   * Return number of block transfers since creation of the log or
   * last reset.
   */
  virtual
  unsigned long blockTransfers() = 0;

  /**
   * Reset block transfer counter.
   */
  virtual
  void resetBlockTransfers() = 0;
  
  /**
   * Get the current timestamp.
   */
  static timestamp now();

protected:
  virtual ~inodeLog() {}
};

//
// strbuf interface
//

inline const strbuf &
strbuf_cat(const strbuf &sb, const inode &i)
{
  sb << "inode:" << i.num;
  return sb;
}


// Blarg.  It isn't working if I just overload strbuf_cat, so overload
// both strbuf_cat and operator<<
inline const strbuf &
strbuf_cat_ts(const strbuf &sb, const timestamp &c)
{
  if (c == TIMESTAMP_LATEST) {
    sb << "LATEST";
  } else {
    char buf[50];
    strftime(buf, 50, "%Y-%m-%d %T", localtime((time_t*)&c));
    sb << buf;
  }

  return sb;
}

inline const strbuf &
strbuf_cat(const strbuf &sb, const timestamp &c)
{
  return strbuf_cat_ts(sb, c);
}

inline const strbuf
operator<< (const strbuf &sb, const timestamp &c)
{
  return strbuf_cat_ts(sb, c);
}

//
// (Un)Marshallers
//

template<>
struct marshaller<timestamp>
{
  marshaller() {}

  str operator() (const timestamp &n) const
  {
    strWriter w;

    w.write(&n, sizeof(timestamp));

    return w.getStr();
  }

  unsigned long maxLength() const
  {
    return sizeof(timestamp);
  }
};

template<>
struct unmarshaller<timestamp>
{
  unmarshaller() {}

  timestamp operator() (str s) const
  {
    strReader r(s);

    timestamp ts;
    r.read(&ts, sizeof(timestamp));
    return ts;
  }
};

template<>
struct marshaller<inumber>
{
  marshaller() {}

  str operator() (const inumber &n) const
  {
    strWriter w;

    w.write(&n, sizeof(inumber));

    return w.getStr();
  }

  unsigned long maxLength() const
  {
    return sizeof(inumber);
  }
};

template<>
struct unmarshaller<inumber>
{
  unmarshaller() {}

  inumber operator() (str s) const
  {
    strReader r(s);

    inumber num;
    r.read(&num, sizeof(inumber));
    return num;
  }
};

template<>
struct marshaller<inode>
{
  marshaller() {}

  str operator() (const inode &n) const 
  {
    strWriter w;

    w.write(&n.num, sizeof(inumber));
    w.write((fattr3*)&n, sizeof(fattr3));
    w.writeStr(n.content);

    return w.getStr();
  }

  unsigned long maxLength() const 
  {
    return 0;
  }
};

template<>
struct unmarshaller<inode>
{
  unmarshaller() {}

  inode operator() (str s) const
  {
    strReader r(s);
    inode node;
    fattr3 fa;

    r.read(&node.num, sizeof(inumber));
    r.read(&fa, sizeof(fattr3));
    node.setFromFattr(fa);
    node.content = r.readStr();

    return node;
  }
};

#endif // INODELOG_H
