package ID3FS::Path;

use strict;
use warnings;
use ID3FS::PathElement::Artist;
use ID3FS::PathElement::Album;
use ID3FS::PathElement::Boolean;
use ID3FS::PathElement::File;
use ID3FS::PathElement::Tag;
use ID3FS::PathElement::Tagval;

our ($STATE_INVALID, $STATE_ROOT, $STATE_TAG, $STATE_TAGVAL,
     $STATE_BOOLEAN, $STATE_ALBUMS, $STATE_TRACKLIST,
     $STATE_FILE)=(0..7);

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

    $self->{db}=shift;
    $self->{path}=shift;
    $self->parse();
#    print "STATE: ", $self->state(), "\n";
    return $self;
}

sub isdir
{
    my($self)=@_;
    if(($self->state() == $STATE_FILE) ||
       ($self->state() == $STATE_INVALID))
    {
	return 0;
    }
    return 1;
}

sub isfile
{
    my($self)=@_;
    return 1 if($self->state() == $STATE_FILE);
    return 0;
}

sub isvalid
{
    my($self)=@_;
    return($self->state() != $STATE_INVALID);
}

sub dest
{
    my($self, $mountpoint)=@_;
    if($self->state() == $STATE_FILE)
    {
	return $self->{db}->filename($mountpoint, @{$self->{elements}});
    }
    return "ERROR"; #should never happen?
}

sub dirents
{
    my($self)=@_;
    my @dents=();
    my $state=$self->state();
#    print "DIRENTS: STATE: $state\n";
#    print "DIRENTS: FILE: $self->{path}\n";
    if($state==$STATE_TAG || $state==$STATE_TAGVAL)
    {
	my $tag=$self->{elements}->[$#{$self->{elements}}];
	if($state==$STATE_TAG &&
	   defined($tag) &&
	   ref($tag) eq "ID3FS::PathElement::Tag" &&
	   $self->{db}->tag_has_values($tag->{id}))
	{
	    @dents=$self->{db}->tags(@{$self->{elements}});
	}
	else
	{
	    @dents=(qw(AND OR TRACKS NOARTIST),
		    $self->{db}->artists(@{$self->{elements}}));
	}
    }
    elsif($state==$STATE_BOOLEAN)
    {
	my $parent=$self->{elements}->[$#{$self->{elements}}];
	unless(defined($parent) &&
	       ref($parent) eq "ID3FS::PathElement::Boolean" &&
	       $parent->{name} eq "NOT")
	{
	    push(@dents, "NOT");
	}
	push(@dents, $self->{db}->tags(@{$self->{elements}}));
    }
    elsif($state==$STATE_ROOT)
    {
	@dents=(qw(ALL NOT), $self->{db}->tags(@{$self->{elements}}));
    }
    elsif($state==$STATE_ALBUMS)
    {
	@dents=(qw(TRACKS NOALBUM),$self->{db}->albums(@{$self->{elements}}));
    }
    elsif($state==$STATE_TRACKLIST)
    {
	@dents=$self->{db}->tracks(@{$self->{elements}});
    }
    else
    {
	print "DIRENTS: UNHANDLED STATE: $state\n";
    }
    return(@dents);
}

sub parse
{
    my($self)=@_;
    @{$self->{components}}=split(/\//, $self->{path});
    shift @{$self->{components}}; # drop empty field before leading /
#    print "PATH: $self->{path}\n";
    $self->state($STATE_ROOT);
    return if($self->{path} eq "/");
    my @parts=@{$self->{components}};
    my($tag, $tagval);
    $self->{elements}=[];
    while(my $name=shift @parts)
    {
#	print "NAME: $name\n";
	my $state=$self->state();
	if($state==$STATE_INVALID)
	{
#	    print "SM: INVALID: $name\n";
	    return;
	}
	elsif($state==$STATE_ROOT)
	{
#	    print "SM: ROOT: $name\n";
	    if($name eq "ALL")
	    {
		$self->state($STATE_TAG);
	    }
	    elsif($name eq "NOT")
	    {
	        push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name));
		$self->state($STATE_BOOLEAN);
	    }
	    else
	    {
		$tag=ID3FS::PathElement::Tag->new($self->{db}, $name);
		if($tag)
		{
		    push(@{$self->{elements}}, $tag);
		    $self->state($STATE_TAG);
		}
		else
		{
		    $self->state($STATE_INVALID);
		}
	    }
	}
	elsif($state==$STATE_TAG || $state==$STATE_TAGVAL)
	{
#	    print "SM: TAG/TAGVAL($state): $name\n";
	    my $tag=$self->{elements}->[$#{$self->{elements}}];
	    if($state==$STATE_TAG &&
	       defined($tag) &&
	       ref($tag) eq "ID3FS::PathElement::Tag" &&
	       $self->{db}->tag_has_values($tag->{id}))
	    {
		my $tagval=ID3FS::PathElement::Tag->new($self->{db}, $name, $tag->{id});
		if(defined($tagval))
		{
		    $self->state($STATE_TAGVAL);
		    # stay in tag state
		    push(@{$self->{elements}}, $tagval);
		}
		else
		{
		    $self->state($STATE_INVALID);
		}
	    }
	    elsif($name eq "TRACKS")
	    {
		$self->state($STATE_TRACKLIST);
	    }
	    elsif($name eq "NOARTIST")
	    {
		$self->state($STATE_TRACKLIST);
	    }
	    elsif($name eq "AND")
	    {
		$self->state($STATE_BOOLEAN);
	        push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name));
	    }
	    elsif($name eq "OR")
	    {
		$self->state($STATE_BOOLEAN);
	        push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name));
	    }
	    else
	    {
		my $artist=ID3FS::PathElement::Artist->new($self->{db}, $name);
		if($artist)
		{
		    push(@{$self->{elements}}, $artist);
		    $self->state($STATE_ALBUMS);
		}
		else
		{
		    $self->state($STATE_INVALID);
		}
	    }
	}
	elsif($state==$STATE_BOOLEAN)
	{
#	    print "SM: BOOLEAN: $name\n";
	    my $parent=$self->{elements}->[$#{$self->{elements}}];
	    my $allownot=1;
	    if(defined($parent) &&
	       ref($parent) eq "ID3FS::PathElement::Boolean" &&
	       $parent->{name} eq "NOT")
	    {
		$allownot=0;
	    }
	    if($allownot && $name eq "NOT")
	    {
		$self->state($STATE_BOOLEAN);
	        push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($self->{db}, $name));
	    }
	    else
	    {
		my $tag=ID3FS::PathElement::Tag->new($self->{db}, $name);
		if($tag)
		{
		    push(@{$self->{elements}}, $tag);
		    $self->state($STATE_TAG);
		}
		else
		{
		    $self->state($STATE_INVALID);
		}
	    }
	}
	elsif($state==$STATE_ALBUMS)
	{
#	    print "SM: ALBUM: $name\n";
	    if($name eq "TRACKS")
	    {
		$self->state($STATE_TRACKLIST);
	    }
	    elsif($name eq "NOALBUM")
	    {
		$self->state($STATE_TRACKLIST);
	    }
	    else
	    {
		my $album=ID3FS::PathElement::Album->new($self->{db}, $name);
		if($album)
		{
		    push(@{$self->{elements}}, $album);
		    $self->state($STATE_TRACKLIST);
		}
		else
		{
		    $self->state($STATE_INVALID);
		}
	    }
	}
	elsif($state==$STATE_TRACKLIST)
	{
#	    print "SM: TRACKLIST: $name\n";
	    my $track=ID3FS::PathElement::File->new($self->{db}, $name);
	    if($track)
	    {
		push(@{$self->{elements}}, $track);
		$self->state($STATE_FILE);
	    }
	    else
	    {
		$self->state($STATE_INVALID);
	    }
	}
	elsif($state==$STATE_FILE)
	{
#	    print "SM: FILE: $name\n";
	    # Can't have anything after a filename
	    $self->state($STATE_INVALID);
	}
	else
	{
	    print "SM: ERROR: UNKNOWN STATE: $self->{state}\n";
	    $self->state($STATE_INVALID);
	}
    }
}

sub state
{
    my($self, $newstate)=@_;
    $self->{state}=$newstate if(defined($newstate));
    return $self->{state};
}

1;
