/*
 * Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
 *
 * Licensed under the BSD License, see LICENSE file for details.
 *
 */

#ifndef CPP_INCLUDE_FUSE_FUSE_ADAPTER_H_
#define CPP_INCLUDE_FUSE_FUSE_ADAPTER_H_

#include <sys/types.h>
#define FUSE_USE_VERSION 26
#include <fuse.h>

#include <boost/scoped_ptr.hpp>
#include <list>
#include <string>

#include "libxtreemfs/system_user_mapping_unix.h"
#include "xtfsutil/xtfsutil_server.h"
#include "xtreemfs/GlobalTypes.pb.h"

namespace xtreemfs {
class Client;
class FuseOptions;
class UserMapping;
class Volume;

namespace pbrpc {
class Stat;
class UserCredentials;
}  // namespace pbrpc

/** Uses fuse_interrupted() to check if an operation was cancelled by the user
 *  and stops retrying to execute the request then.
 *
 * Always returns 0, if called from a non-Fuse thread. */
int CheckIfOperationInterrupted();

class FuseAdapter {
 public:
  /** Creates a new instance of FuseAdapter, but does not create any libxtreemfs
   *  Client yet.
   *
   *  Use Start() to actually create the client and mount the volume given in
   *  options. May modify options.
   */
  explicit FuseAdapter(FuseOptions* options);

  ~FuseAdapter();

  /** Create client, open volume and start needed threads.
   * @return Returns a list of additional "-o<option>" Fuse options which may be
   *         generated after processing the "options" parameter and have to be
   *         considered before starting Fuse.
   * @remark Ownership of the list elements is transferred to the caller. */
  void Start(std::list<char*>* required_fuse_options);

  /** Shutdown threads, close Volume and Client and blocks until all threads are
   *  stopped. */
  void Stop();

  /** After successfully executing fuse_new, tell libxtreemfs to use
   *  fuse_interrupted() if a request was cancelled by the user. */
  void SetInterruptQueryFunction() const;

  void GenerateUserCredentials(
      uid_t uid,
      gid_t gid,
      pid_t pid,
      xtreemfs::pbrpc::UserCredentials* user_credentials);

  /** Generate UserCredentials using information from fuse context or the
   *  current process (in that case set fuse_context to NULL). */
  void GenerateUserCredentials(
      struct fuse_context* fuse_context,
      xtreemfs::pbrpc::UserCredentials* user_credentials);

  /** Fill a Fuse stat object with information from an XtreemFS stat. */
  void ConvertXtreemFSStatToFuse(const xtreemfs::pbrpc::Stat& xtreemfs_stat,
                                 struct stat* fuse_stat);

  /** Converts given UNIX file handle flags into XtreemFS symbols. */
  xtreemfs::pbrpc::SYSTEM_V_FCNTL ConvertFlagsUnixToXtreemFS(int flags);

  /** Converts from XtreemFS error codes to the system ones. */
  int ConvertXtreemFSErrnoToFuse(xtreemfs::pbrpc::POSIXErrno xtreemfs_errno);

  // Fuse operations as called by placeholder functions in fuse_operations.h. */
  int statfs(const char *path, struct statvfs *statv);
  int getattr(const char *path, struct stat *statbuf);
  int getxattr(const char *path, const char *name, char *value, size_t size);

  /** Creates CachedDirectoryEntries struct and let fi->fh point to it. */
  int opendir(const char *path, struct fuse_file_info *fi);

  /** Uses the Fuse readdir offset approach to handle readdir requests in chunks
   *  instead of one large request. */
  int readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
              struct fuse_file_info *fi);

  /** Deletes CachedDirectoryEntries struct which is hold by fi->fh. */
  int releasedir(const char *path, struct fuse_file_info *fi);

  int utime(const char *path, struct utimbuf *ubuf);
  int utimens(const char *path, const struct timespec tv[2]);
  int create(const char *path, mode_t mode, struct fuse_file_info *fi);
  int mknod(const char *path, mode_t mode, dev_t device);
  int mkdir(const char *path, mode_t mode);
  int open(const char *path, struct fuse_file_info *fi);
  int truncate(const char *path, off_t newsize);
  int ftruncate(const char *path, off_t offset, struct fuse_file_info *fi);
  int write(const char *path, const char *buf, size_t size, off_t offset,
            struct fuse_file_info *fi);
  int flush(const char *path, struct fuse_file_info *fi);
  int read(const char *path, char *buf, size_t size, off_t offset,
           struct fuse_file_info *fi);
  int access(const char *path, int mask);
  int unlink(const char *path);
  int fgetattr(const char *path, struct stat *statbuf,
               struct fuse_file_info *fi);
  int release(const char *path, struct fuse_file_info *fi);

  int readlink(const char *path, char *buf, size_t size);
  int rmdir(const char *path);
  int symlink(const char *path, const char *link);
  int rename(const char *path, const char *newpath);
  int link(const char *path, const char *newpath);
  int chmod(const char *path, mode_t mode);
  int chown(const char *path, uid_t uid, gid_t gid);

  int setxattr(const char *path, const char *name, const char *value,
               size_t size, int flags);
  int listxattr(const char *path, char *list, size_t size);
  int removexattr(const char *path, const char *name);

  int lock(const char* path, struct fuse_file_info *fi, int cmd,
           struct flock* flock);

 private:
  /** Contains all needed options to mount the requested volume. */
  FuseOptions* options_;

  /** Translates between local and remote usernames and groups. */
  SystemUserMappingUnix system_user_mapping_;

  /** Created libxtreemfs Client. */
  boost::scoped_ptr<Client> client_;

  /** Opened libxtreemfs Volume. */
  Volume* volume_;

  /** Server for processing commands sent from the xtfsutil tool
      via xctl files. */
  XtfsUtilServer xctl_;
};

}  // namespace xtreemfs

#endif  // CPP_INCLUDE_FUSE_FUSE_ADAPTER_H_