//
// 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"
#include "lock_client.h"

// #define FSLOCKS
//#define LAB2

class fs {
 public:
//  fs(blockdbc *db);
  fs(ref<tagged_blockdbc> bdb, ref<lock_client> lc);
  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();
  ref<lock_client> get_lc();
  
 private:
  blockdbc *db;
  ref<tagged_blockdbc> bdb;
  ref<lock_client> lc;
  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 flush_on_flush(callback<void,void>::ref cb, int r);

  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_acquire(nfscall *nc);
  void nfs3_getattr_on_get_fattr(nfscall *nc, ref<fsfile> file,
                                 bool ok, fattr3 fa);
  void nfs3_setattr_on_acquire(nfscall *nc);
  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_acquire(nfscall *nc);
  void nfs3_access_on_get_fattr(nfscall *nc, ref<fsfile> file,
                                bool ok, fattr3 fa);
  void nfs3_lookup_on_acquire(nfscall *nc);
  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_acquire(nfscall *nc);
  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_acquire(nfscall *nc);
  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_acquire(nfscall *nc);
  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_create_exists_on_get_fattr(nfscall *nc, ref<fsdir> dir,
                                       fattr3 dirfa,
                                       ref<fsfile> file,
                                       bool ok, fattr3 filefa);
  void nfs3_create_exists_on_set_fattr(nfscall *nc, ref<fsdir> dir,
                                       fattr3 dirfa,
                                       ref<fsfile> file,
                                       bool ok, fattr3 filefa);
  void nfs3_mkdir_on_acquire(nfscall *nc);
  void nfs3_mkdir_on_get_fattr(nfscall *nc, ref<fsdir> dir, bool ok,
                               fattr3 oldpfa);
  void nfs3_mkdir_on_set_fattr(nfscall *nc, ref<fsdir> pdir, fattr3 oldpfa,
                               bool ok, fattr3 newpfa);
  void nfs3_mkdir_on_get_dircontents(nfscall *nc, ref<fsdir> pdir,
                                     fattr3 oldpfa,
                                     fattr3 newpfa,
                                     int r, ptr<fsdirlist> dircontents);
  void nfs3_mkdir_on_create(nfscall *nc, ref<fsdir> pdir, fattr3 oldpfa,
                            fattr3 newpfa, ref<fsdir> ndir,
                            int r, fattr3 nfa);
  void nfs3_mkdir_on_add_file_dir(nfscall *nc, ref<fsdir> pdir, fattr3 oldpfa,
                                  fattr3 newpfa, ref<fsdir> ndir,
                                  int r);
  void nfs3_mkdir_on_add_file_dot(nfscall *nc, ref<fsdir> pdir, fattr3 oldpfa,
                                  fattr3 newpfa, ref<fsdir> ndir,
                                  int r);
  void nfs3_mkdir_on_add_file_dotdot(nfscall *nc, ref<fsdir> pdir,
                                     fattr3 oldpfa,
                                     fattr3 newpfa, ref<fsdir> ndir,
                                     int r);
  void nfs3_remove_on_acquire(nfscall *nc);
  void nfs3_remove_on_get_dir_fattr(nfscall *nc, ref<fsdir> dir,
                                    bool ok, fattr3 oldfa);
  void nfs3_remove_on_get_dircontents(nfscall *nc, ref<fsdir> dir,
                                      fattr3 olddfa,
                                      int r, ptr<fsdirlist> dircontents);
  void nfs3_remove_on_get_file_fattr(nfscall *nc, ref<fsdir> dir,
                                     fattr3 olddfa,
                                     ptr<fsdirlist> dircontents,
                                     ref<fsfile> file,
                                     bool ok, fattr3 filefa);
  void nfs3_remove_on_set_file_fattr(nfscall *nc, ref<fsdir> dir,
                                     fattr3 olddfa,
                                     ptr<fsdirlist> dircontents,
                                     ref<fsfile> file,
                                     bool ok, fattr3 filefa);
  void nfs3_remove_on_set_dircontents(nfscall *nc, ref<fsdir> dir,
                                      fattr3 olddfa,
                                      ptr<fsdirlist> dircontents,
                                      ref<fsfile> file,
                                      int r);
  void nfs3_remove_on_get_final_fattr(nfscall *nc, ref<fsdir> dir,
                                      fattr3 olddfa,
                                      ptr<fsdirlist> dircontents,
                                      ref<fsfile> file,
                                      bool ok, fattr3 newdfa);
  void nfs3_readdir_on_acquire(nfscall *nc);
  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 flush(str lock, callback<void,void>::ref cb);

  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
