/*
 * File module
 *
 * This module provides an abstraction for working with files in the
 * file system. It bundles together a file's inode and its contents
 * and abstracts performing operations on the file.
 *
 * Locking is implemented via file object lifetime.  The first time an
 * operation is performed on a file object, it will take the lock.
 * The lock will be released when the object is freed.  When the lock
 * is released (assuming it was a write lock), any updates should be
 * flushed.
 */

#ifndef FSFILE_H
#define FSFILE_H

#include "persifs.h"
#include "fs.h"
#include "inodelog.h"
#include "chunkable.h"

class fsfile
{
public:
  /**
   * Create a new, empty file with the specified attributes.  This is
   * a factory function.
   */
  static void create(ref<FS> fs, fattr3 fa,
                     callback<void, ref<fsfile> >::ref cb, cbe error);

  /**
   * Load an existing file, given its inumber.  Flags can be any
   * combination of the L_* flags listed below.  A file cannot be
   * opened for write unless ts is TIMESTAMP_LATEST.
   */
  static void load(ref<FS> fs, timestamp ts, inumber num, int flags,
                   callback<void, ref<fsfile> >::ref cb, cbe error);
  // Open the file for reading.  This is implied.
  static const int L_READ = 0;
  // Open the file for writing.  This will cause it to take a write
  // lock when any operation is performed.
  static const int L_WRITE = 1;
  // Take the lock for this file immediately instead of lazily.
  static const int L_LOCKNOW = 2;

  /**
   * Get the filesystem backing this file.
   */
  ref<FS> getFS();
  /**
   * Get the inumber of this file.
   */
  inumber getInumber();

  /**
   * Read a range from the file.
   */
  void read(uint pos, uint len, callback<void, str>::ref cb, cbe error);
  /**
   * Overwrite a substring of this file.  If this extends beyond the
   * end of the file, the length of the file is extended.
   */
  void write(uint pos, str data, callback<void>::ref cb, cbe error);
  /**
   * Get the attributes of this file.
   */
  void getAttr(callback<void, fattr3>::ref cb, cbe error);
  /**
   * Set the attributes of this file.  The size attribute is special.
   * If the actual file size is less than fa.size, the file gets
   * truncated. If it's larger, the file gets padded with NULLs up to
   * len.  This updates the ctime of the attributes.  Passes the
   * previous attributes and the newly set attributes to the callback.
   */
  
  void setAttr(fattr3 fa, callback<void, fattr3, fattr3>::ref cb, cbe error);

protected:
  /**
   * If the lock has been taken for this file, release it.
   */
  virtual ~fsfile();

  fsfile(ref<FS> fs, ptr<chunkable> content, inode ino, timestamp ts);

  void acquireLock(bool load, callback<void>::ref cb, cbe error);
  
private:
  ref<FS> fs;
  ptr<chunkable> content;       // can be NULL before lock is acquired
  inode ino;
  timestamp ts;
  bool writable;
  bool lockHeld;

  // Callbacks
  static void create_after_chunkableCreate(
    ref<FS> fs, fattr3 fa,
    callback<void, ref<fsfile> >::ref cb,
    cbe error,
    ref<chunkable> chunk);
  static void create_after_newInumber(
    ref<FS> fs, ref<chunkable> chunk,
    inode ino,
    callback<void, ref<fsfile> >::ref cb,
    cbe error, inumber num);
  static void create_after_acquireLock(
    callback<void, ref<fsfile> >::ref cb,
    cbe error, ref<fsfile> file);
  static void loadLatest(
    ref<FS> fs, inumber num, int flags,
    callback<void, ref<fsfile> >::ref cb, cbe error);
  static void loadLatest_after_acquireLock(
    ref<fsfile> file,
    callback<void, ref<fsfile> >::ref cb,
    cbe error);
  static void loadPast(
    ref<FS> fs, timestamp ts, inumber num, int flags,
    callback<void, ref<fsfile> >::ref cb, cbe error);
  static void loadPast_after_logGet(
    ref<FS> fs, timestamp ts, int flags,
    callback<void, ref<fsfile> >::ref cb,
    cbe error, inode ino);
  static void loadPast_after_unmarshal(
    ref<FS> fs, timestamp ts,
    inode ino,
    callback<void, ref<fsfile> >::ref cb,
    cbe error,  ref<chunkable> content);
  void read_after_acquireLock(
    uint pos, uint len,
    callback<void, str>::ref cb, cbe error);
  void getAttr_after_acquireLock(
    callback<void, fattr3>::ref cb, cbe error);
  void write_after_acquireLock(uint pos, str data,
                               callback<void>::ref cb, cbe error);
  void write_after_writeSubstr(uint pos, str data,
                               callback<void>::ref cb, cbe error);
  void write_after_length(uint pos, str data,
                          callback<void>::ref cb, cbe error,
                          unsigned long len);
  void setAttr_after_acquireLock(
    fattr3 fa,
    callback<void, fattr3, fattr3>::ref cb, cbe error);
  void setAttr_after_truncate(
    fattr3 fa,
    callback<void, fattr3, fattr3>::ref cb, cbe error);
  void acquireLock_after_acquire(bool load,
                                 callback<void>::ref cb, cbe error);
  void acquireLock_after_inodeGet(callback<void>::ref cb, cbe error,
                                  inode ino);
  void acquireLock_after_unmarshall(callback<void>::ref cb, cbe error,
                                    ref<chunkable> ch);
};


#endif // FSFILE_H
