/*
 * 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.
   */
  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.
   */
  void setAttr(fattr3 fa, callback<void, fattr3>::ref cb, cbe error);
  
  /**
   * If the actual file size is less than len, truncate it. Otherwise,
   * pad the file with NULLs up to len.
   */
  void setLength(unsigned long len,  callback<void>::ref cb, cbe error);

protected:
  /**
   * Constructor
   */
  fsfile(ref<chunkable> content, inode in, ref<FS> fs);
  
  /**
   * If the lock has been taken for this file, release it.
   */
  ~fsfile();
  
 
private:
  ref<chunkable> content;
  inode in;
  ref<FS> my_fs;
  friend class fsdir; 
  /**
   * Callbacks
   */
  static void create_cb1(ref<FS> fs, fattr3 fa, callback<void, ref<fsfile> >::ref cb, cbe error, ref<chunkable> contents);
  
  static void create_cb2(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> contents, inode in, cbe error, inumber num);
  
  static void create_cb3(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> contents, inode in, cbe error, marshalled m_data);
  
  static void create_cb4(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> contents, inode in, cbe error);
  
  static void create_cb5(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> content, inode in, cbe error, timestamp ts);
  
  
  
  void read_cb1(callback<void,str>::ref cb, cbe error, str data);


  void setLength_cb1(callback<void>::ref cb, cbe error);
  void setLength_cb2(callback<void>::ref cb, cbe error, unsigned long len);
  void setLength_cb3(callback<void>::ref cb, cbe error, marshalled m_data);
  void setLength_cb4(callback<void>::ref cb, cbe error);
  

  void write_cb1(callback<void>::ref cb, cbe error);
  void write_cb2(callback<void>::ref cb, cbe error, unsigned long len);
  void write_cb3(callback<void>::ref cb, cbe error, marshalled m_data);
  void write_cb4(callback<void>::ref cb, cbe error);
 

  static void load_cb1(ref<FS> fs, int flags, callback<void, ref<fsfile> >::ref cb, cbe error, inode in);
  static void load_cb2(ref<FS> fs, inode in, callback<void, ref<fsfile> >::ref cb, cbe error, ref<chunkable> content);
  static void load_cb3(ref<FS> fs, inode in, ref<chunkable> content, callback<void, ref<fsfile> >::ref cb, cbe error);
  


};

#endif // FSFILE_H









