//
// file server
// accepts NFS calls via dispatch()
// uses a class blockdb for storage
//

#ifndef FS_H
#define FS_H

#include "amisc.h"
#include "async.h"
#include "arpc.h"
#include "nfsserv.h"
#include "blockdbc.h"
#include "tagged_blockdbc.h"
#include "fsfile.h"
#include "fsdir.h"

// #define FSLOCKS
//#define LAB2

class fs {
 public:
//  fs(blockdbc *db);
  fs(ref<tagged_blockdbc> bdb);
  void dispatch(nfscall *);
  void new_root(char *fhname, callback<void, bool, nfs_fh3>::ref cb);
  void test_rw(nfs_fh3 fh, callback<void, bool>::ref cb);
  void test_dir(nfs_fh3 fh, callback<void, bool>::ref cb);
  ref<tagged_blockdbc> get_bdb();
  
 private:
  blockdbc *db;
  ref<tagged_blockdbc> bdb;
  unsigned long long seq; // used to number new i-nodes &c
  static const int FHLEN = 8; // file handles are this many bytes long
  ptr<fsdir> fsroot;

  void nfs3_getattr(nfscall *nc);
  void nfs3_lookup(nfscall *nc);
  void nfs3_access(nfscall *nc);
  void nfs3_write(nfscall *nc);
  void nfs3_readdir(nfscall *nc);
  void nfs3_create(nfscall *nc);
  void nfs3_fsinfo(nfscall *nc);
  void nfs3_fsstat(nfscall *nc);
  void nfs3_read(nfscall *nc);
  void nfs3_setattr(nfscall *nc);
  void nfs3_remove(nfscall *nc);
  void nfs3_mkdir(nfscall *nc);

  void nfs3_getattr_on_get_fattr(nfscall *nc, ref<fsfile> file,
                                 bool ok, fattr3 fa);
  void nfs3_setattr_on_get_fattr(nfscall *nc, ref<fsfile> file,
                                 bool ok, fattr3 oldfa);
  void nfs3_setattr_on_set_fattr(nfscall *nc, ref<fsfile> file, fattr3 oldfa,
                                 bool ok, fattr3 newfa);
  void nfs3_access_on_get_fattr(nfscall *nc, ref<fsfile> file,
                                bool ok, fattr3 fa);
  void nfs3_lookup_on_get_fattr(nfscall *nc, ref<fsdir> dir,
                                bool ok, fattr3 fa);
  void nfs3_lookup_on_get_dircontents(nfscall *nc, ref<fsdir> dir,
                                      int r, ptr<fsdirlist> lst);
  void nfs3_write_on_get_fattr(nfscall *nc, ref<fsfile> file,
                               bool ok, fattr3 oldfa);
  void nfs3_write_on_write(nfscall *nc, ref<fsfile> file, fattr3 oldfa,
                           bool ok);
  void nfs3_write_on_get_fattr_again(nfscall *nc, ref<fsfile> file,
                                     fattr3 oldfa, bool ok, fattr3 newfa);
  void nfs3_read_on_get_fattr(nfscall *nc, ref<fsfile> file, bool ok,
                              fattr3 fa);
  void nfs3_read_on_read(nfscall *nc, ref<fsfile> file, fattr3 fa,
                         bool ok, str data);
  void nfs3_create_on_get_fattr(nfscall *nc, ref<fsdir> dir,
                                bool ok, fattr3 fa);
  void nfs3_create_on_get_dircontents(nfscall *nc, ref<fsdir> dir,
                                      fattr3 dirfa,
                                      int r, ptr<fsdirlist> lst);
  void nfs3_create_on_create(nfscall *nc, ref<fsdir> dir, fattr3 dirfa,
                             ref<fsfile> file, int r, fattr3 fa);
  void nfs3_create_on_add_file(nfscall *nc, ref<fsdir> dir, fattr3 dirfa,
                               ref<fsfile> file, int r);
  void nfs3_create_on_final_get_fattr(nfscall *nc, ref<fsdir> dir,
                                      fattr3 dirfa, ref<fsfile> file,
                                      bool ok, fattr3 newfa);
  void nfs3_readdir_on_get_fattr(nfscall *nc, ref<fsdir> dir,
                                 bool ok, fattr3 fa);
  void nfs3_readdir_on_get_dircontents(nfscall *nc, ref<fsdir> dir,
                                       int r, ptr<fsdirlist> lst);
  
  
  void new_fh(nfs_fh3 *fh);
  void new_root_on_get_fattr(callback<void, bool, nfs_fh3>::ref cb,
                             nfs_fh3 fh, fattr3 newfa,
                             bool ok, fattr3 oldfa);
  void new_root_on_create(callback<void, bool, nfs_fh3>::ref cb,
                          nfs_fh3 fh,
                          int r, fattr3 fa);
  uint64 fh2fileid(nfs_fh3 fh);
  wcc_data make_wcc(fattr3 before, fattr3 after);
  void remove_fh(nfs_fh3 fh, callback<void, bool>::ref cb);
  void put_new_fh(nfs_fh3 fh, fattr3 fa, callback<void, bool, fattr3>::ref cb);
  void flush(str lock, callback<void,void>::ref cb);
  void unpack_dirent(str dirent, bool &allocated, nfs_fh3 &fh, str &name);
  str pack_dirent(bool allocated, nfs_fh3 fh, str name);

  void test_rw_on_write(nfs_fh3 fh, callback<void, bool>::ref cb,
                        ref<fsfile> file, str teststr,
                        bool ok);
  void test_rw_on_read(nfs_fh3 fh, callback<void, bool>::ref cb,
                       ref<fsfile> file, str teststr,
                       bool ok, str data);
  void test_dir_on_add1(nfs_fh3 fh, callback<void, bool>::ref cb,
                        ref<fsdir> dir,
                        int r);
  void test_dir_on_add2(nfs_fh3 fh, callback<void, bool>::ref cb,
                        ref<fsdir> dir,
                        int r);
  void test_dir_on_get_dircontents(nfs_fh3 fh, callback<void, bool>::ref cb,
                                   ref<fsdir> dir,
                                   int r, ptr<fsdirlist> lst);
};

#endif
