package ID3FS::Fuse;

use strict;
use warnings;
use ID3FS::Path;
use Fuse;
use Cwd;
use POSIX qw(EINVAL EROFS ENOENT EOPNOTSUPP S_IRUSR S_IRGRP S_IROTH S_IXUSR S_IXGRP S_IXOTH);
our ($TYPE_DIR, $TYPE_SYMLINK)=(0040, 0120);

sub new
{
    my $proto=shift;
    my $class=ref($proto) || $proto;
    my $self={};
    bless($self,$class);

    $self->{db}=shift;
    $self->{source}=shift;
    $self->{mountpoint}=Cwd::abs_path(shift);
    $self->{debug}=shift;
    $self->{perms} = S_IRUSR() | S_IXUSR() | S_IRGRP() | S_IXGRP() | S_IROTH() | S_IXOTH();

    return $self;
}

sub run
{
    my($self)=@_;
    Fuse::main(
	mountpoint  => $self->{mountpoint},
	threaded    => 0,
	debug       => $self->{debug},
	mountopts   => "allow_other,ro",
	getattr     => sub { $self->getattr(@_); },
	readlink    => sub { $self->readlink(@_); },
	getdir      => sub { $self->getdir(@_); },

	# Not used
#	mknod       => sub { $self->mknod(@_);       },
#	mkdir       => sub { $self->mkdir(@_);       },
#	unlink      => sub { $self->unlink(@_);      },
#	rmdir       => sub { $self->rmdir(@_);       },
#	symlink     => sub { $self->symlink(@_);     },
#	rename      => sub { $self->rename(@_);      },
#	link        => sub { $self->link(@_);        },
#	chmod       => sub { $self->chmod(@_);       },
#	chown       => sub { $self->chown(@_);       },
#	truncate    => sub { $self->truncate(@_);    },
#	utime       => sub { $self->utime(@_);       },
#	open        => sub { $self->open(@_);        },
#	read        => sub { $self->read(@_);        },
#	write       => sub { $self->write(@_);       },
#	statfs      => sub { $self->statfs(@_);      },
#	release     => sub { $self->release(@_);     },
#	fsync       => sub { $self->fsync(@_);       },
#	setxattr    => sub { $self->setxattr(@_);    },
#	getxattr    => sub { $self->getxattr(@_);    },
#	listxattr   => sub { $self->listxattr(@_);   },
#	removexattr => sub { $self->removexattr(@_); },
	);
}

sub getattr
{
    my($self, $filename)=@_;
#    print "**GETATTR: $filename\n";
    my $path=ID3FS::Path->new($self->{db}, $filename);
    my $last_update=$self->{db}->last_update();
    return(-ENOENT()) unless($path->isvalid());
    my($dev,$ino,$nlink)=(0,0,1);
    my $uid=$<;
    my $gid=(split(/ /, $( ))[0];
    my($rdev,$size)=(0,1);
    my($atime,$mtime,$ctime)=($last_update) x 3;
    my($blksize,$blocks)=(512,1);
    my $mode=$self->mode( $path->isdir() ? $TYPE_DIR : $TYPE_SYMLINK );
    return($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
	   $atime, $mtime, $ctime, $blksize, $blocks);
}

sub readlink
{
    my($self,$filename)=@_;
#    print "**READLINK: $filename\n";
    my $path=ID3FS::Path->new($self->{db}, $filename);
    return(-EINVAL()) unless($path->isfile());
    return $path->dest($self->{mountpoint});
}

sub getdir
{
    my($self, $filename)=@_;
#    print "**GETDIR: $filename\n";
    my $path=ID3FS::Path->new($self->{db}, $filename);
    return(-ENOENT()) unless($path->isvalid());
    return(-ENOTDIR()) unless($path->isdir());
    my @dents=(".", "..");
    my($dirs, $files)=$path->dirents();
    push(@dents, grep { $self->hascontents($filename, $_); } @$dirs);
    push(@dents, @$files);
    return(@dents, 0);
}

sub hascontents
{
    return 1;
    # FIXME
    my($self, $base, $dir)=@_;
    print "hascontents: $base / $dir\n";
    my $path=ID3FS::Path->new($self->{db}, "$base/$dir");
    print "VALID: ", $path->isvalid(), "\n";
    return 1 unless($path->isvalid());
    my($subdirs,$subfiles)=$path->dirents();
    print "SUBDIRS: ", join(", ", @$subdirs), "\n";
    print "SUBFILES: ", join(", ", @$subfiles), "\n";
    return 1 if(@$subdirs || @$subfiles);
    return 0;
}

# unused stubs
sub mknod       { print "FUSE: mknod\n";       return -EROFS();      }
sub mkdir       { print "FUSE: mkdir\n";       return -EROFS();      }
sub unlink      { print "FUSE: unlink\n";      return -EROFS();      }
sub rmdir       { print "FUSE: rmdir\n";       return -EROFS();      }
sub symlink     { print "FUSE: symlink\n";     return -EROFS();      }
sub rename      { print "FUSE: rename\n";      return -EROFS();      }
sub link        { print "FUSE: link\n";        return -EROFS();      }
sub chmod       { print "FUSE: chmod\n";       return -EROFS();      }
sub chown       { print "FUSE: chown\n";       return -EROFS();      }
sub truncate    { print "FUSE: truncate\n";    return -EROFS();      }
sub utime       { print "FUSE: utime\n";       return -EINVAL();     }
sub open        { print "FUSE: open\n";        return -EINVAL();     }
sub read        { print "FUSE: read\n";        return -EINVAL();     }
sub write       { print "FUSE: write\n";       return -EROFS();      }
sub statfs      { print "FUSE: statfs\n";      return -EINVAL();     }
sub release     { print "FUSE: release\n";     return 0;             }
sub fsync       { print "FUSE: fsync\n";       return 0;             }
sub setxattr    { print "FUSE: setxattr\n";    return -EOPNOTSUPP(); }
sub getxattr    { print "FUSE: getxattr\n";    return -EOPNOTSUPP(); }
sub listxattr   { print "FUSE: listxattr\n";   return -EOPNOTSUPP(); }
sub removexattr { print "FUSE: removexattr\n"; return -EOPNOTSUPP(); }

sub mode
{
    my($self, $type)=@_;
    return(($type << 9) | $self->{perms});
}

1;
