#!/usr/bin/python

from __future__ import with_statement

import errno
import os
import os.path
import sys

import fuse

import backshift_file_mod
import compressed_file_mod
import db_mod
import repo_mod
import saveset_summary_mod


class Backshift_fuse(fuse.LoggingMixIn, fuse.Operations):    
    def __init__(self, backup_id):
        self.backup_id = backup_id
        self.canonical_hostname = saveset_summary_mod.canonicalize_hostname()
        self.repo = repo_mod.Repo(save_directory='.', canonical_hostname=self.canonical_hostname, subset=None)
        self.repo.to_established_backup_id(self.backup_id)

    def __call__(self, op, path, *args):
        return super(Backshift_fuse, self).__call__(op, path, *args)

    def access(self, path, mode):
        if not os.access(path, mode):
            raise fuse.FuseOSError(errno.EACCES)

    def chmod(self, path, mode):
        raise fuse.FuseOSError(errno.EROFS)

    def chown(self, path, uid, gid):
        raise fuse.FuseOSError(errno.EROFS)

    def create(self, path, mode):
        raise fuse.FuseOSError(errno.EROFS)

    def flush(self, path, fh):
        raise fuse.FuseOSError(errno.EROFS)

    def fsync(self, path, datasync, fh):
        raise fuse.FuseOSError(errno.EROFS)

    def getattr(self, path, fh=None):
        print('in getattr!')
        basename = os.path.basename(path)
        dirname = os.path.dirname(path)
        print('basename: %s, dirname: %s' % (basename, dirname))
        directory = repo_mod.prepare_initial_directory(self.backup_id, dirname)
        print('directory: %s' % directory)
        plus_entries = os.path.join(directory, 'entries')
        directory_content = db_mod.open(plus_entries, 'rb', backend_open=compressed_file_mod.Compressed_file)
        print('directory_content: %s' % str(directory_content.items()))
        try:
            if basename in directory_content:
                self.backshift_file = backshift_file_mod.Backshift_file(self.repo, directory_content[basename], directory_content)
                keys = ('st_ctime', 'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid')
                result = dict((key, getattr(self.backshift_file, key)) for key in keys)
                return result
            else:
                print('basename was not in keys(): %s' % basename)
                print('directory_content.keys)): %s' % str(directory_content.keys()))
                raise fuse.FuseOSError(errno.ENOENT)
        finally:
            directory_content.close()

        # DRS: Clearly, this needs to look in the repo instead of the filesystem

    def getxattr(self, *args, **kwargs):
        raise fuse.FuseOSError(errno.ENOTSUP)

    def link(self, target, source):
        raise fuse.FuseOSError(errno.EROFS)

    def listxattr(self, *args, **kwargs):
        raise fuse.FuseOSError(errno.ENOTSUP)

    def mkdir(self, path, mode=None):
        raise fuse.FuseOSError(errno.EROFS)

    def mknod(self, filename, node=None, device=None):
        raise fuse.FuseOSError(errno.EROFS)

    def open(self, full_path, flag, mode=None):
        # DRS: Still need to make this read a backshift file
        if mode is not None and ('w' in mode or '+' in mode):
            raise fuse.FuseOSError(errno.EROFS)
        directory = os.path.dirname(full_path)
        filename = os.path.basename(full_path)
        directory_content = db_mod.open(directory, 'rb', backend_open=compressed_file_mod.Compressed_file)
        self.backshift_file = backshift_file_mod.Backshift_file(self.repo, directory_content[filename], full_path)

    def read(self, full_path, size, offset, fh):
        left = offset
        right = left + size - 1
        try:
            return repo_mod.give_block(self.backshift_file, left, right)
        except repo_mod.Not_regular_file:
            raise fuse.FuseOSError(errno.ENOTSUP)

    def readdir(self, full_path, fh):
        directory = repo_mod.prepare_initial_directory(self.backup_id, full_path)
        entries_filename = os.path.join(directory, 'entries')
        directory_content = db_mod.open(entries_filename, 'rb', backend_open=compressed_file_mod.Compressed_file)
        list_ = list(directory_content)
        directory_content.close()
        yield '.'
        yield '..'
        for element in list_:
            # FIXME: Why do we get back null strings sometimes?
            if element != '':
                yield element

    # DRS: Still need to respect the appropriate file
    readlink = os.readlink

    def release(self, path, fh):
        return os.close(fh)

    def rename(self, old, new):
        raise fuse.FuseOSError(errno.EROFS)

    def rmdir(self, path):
        raise fuse.FuseOSError(errno.EROFS)

    def statfs(self, path):
        raise fuse.FuseOSError(errno.ENODATA)

    def symlink(self, target, source):
        raise fuse.FuseOSError(errno.EROFS)

    def truncate(self, path, length, fh=None):
        raise fuse.FuseOSError(errno.EROFS)

    def unlink(self, path):
        raise fuse.FuseOSError(errno.EROFS)

    def utimens(self, path, other=None):
        raise fuse.FuseOSError(errno.EROFS)

    def write(self, path, data, offset, fh):
        raise fuse.FuseOSError(errno.EROFS)


def usage(retval):
    sys.stderr.write('%s --save-directory /save/dir --backup-id backup_id --help\n' % sys.argv[0])
    sys.exit(retval)


def main():
    save_directory = None
    backup_id = None
    mount_point = None
    while sys.argv[1:]:
        if sys.argv[1] == '--save-directory':
            save_directory = sys.argv[2]
            del sys.argv[1]
        elif sys.argv[1] == '--backup-id':
            backup_id = sys.argv[2]
            del sys.argv[1]
        elif sys.argv[1] == '--mount-point':
            mount_point = sys.argv[2]
            del sys.argv[1]
        elif sys.argv[1] in [ '-h', '--help' ]:
            usage(0)
        else:
            sys.stderr.write('%s: Unrecognized option: %s\n' % (sys.argv[0], sys.argv[1]))
            usage(1)
        del sys.argv[1]
    if save_directory is None:
        sys.stderr.write('%s: --save-directory is a required option\n' % sys.argv[0])
        usage(1)
    if backup_id is None:
        sys.stderr.write('%s: --backup-id is a required option\n' % sys.argv[0])
        usage(1)
    if mount_point is None:
        sys.stderr.write('%s: --mount-point is a required option\n' % sys.argv[0])
        usage(1)
    os.chdir(save_directory)
    _filesystem = fuse.FUSE(Backshift_fuse(backup_id=backup_id), mount_point, foreground=True)


if __name__ == "__main__":
    main()