package ID3FS::Path::Node;

use strict;
use warnings;

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

    my $db=shift;
    $self->{type}=shift;
    $self->{name}=shift;
    $self->{parents_id}=shift;
    if($self->{type} ne "boolean")
    {
	my $table=ucfirst($self->{type});
	$table .= "s" unless($table=~/s$/);
	$self->{id}=$db->lookup_id($table, $self->{name}, $self->{parents_id});
	return undef unless(defined($self->{id}));
    }
    return $self;
}

sub set
{
    my($self, $name, $val)=@_;
    if(defined($val))
    {
	$self->{$name}=$val;
    }
    return $self->{$name};
}

sub left       { return shift->set("left",       shift); }
sub right      { return shift->set("right",      shift); }
sub name       { return shift->set("name",       shift); }
sub type       { return shift->set("type",       shift); }
sub id         { return shift->set("id",         shift); }
sub parents_id { return shift->set("parents_id", shift); }

sub print
{
    my($self)=@_;
    my $op=$self->name();
    my $left=$self->left();
    my $right=$self->right();
    return "" unless($left || $right);
    my $str .= $self->print_node($left);
    $str .= (" " . $op . " ") if($op);
    $str .= $self->print_node($right);
    if($op || ($left && $right))
    {
	$str="(" . $str . ")";
    }
    return $str;
}

sub print_node
{
    my($self, $node)=@_;
    return "" unless(defined($node));
    return $node->print() if(ref($node) eq "ID3FS::Path::Node");
    return $node->{name};
}

sub to_sql
{
    my($self, $hasvals, $not, @joins)=@_;
    $not=0 unless(defined($not));
    my @outjoins=();
    my $str='';
    # init
    unless(@joins)
    {
	@outjoins = @joins = ("INNER");
    }
    if($self->type() ne "boolean")
    {
	my $cnt=scalar(@joins)+1;
	$str .= "t" . scalar(@joins) . ".id='" . $self->{id} . "'";
	if($not && !$hasvals)
	{
	    $str .= " AND fxt" . scalar(@joins) . ".files_id IS NULL";
	}
	return ($str, @outjoins);
    }
    else
    {
	my $left=$self->left();
	my $right=$self->right();
	return ("", @outjoins) unless($left || $right);
	my ($leftstr, @leftjoins) = $left->to_sql($hasvals, $not, @joins) if($left);
	push(@joins, @leftjoins);
	push(@outjoins, @leftjoins);
	my $op=$self->name();
	if(defined($op) && $self->type() eq "boolean")
	{
	    # if we are ANDing, add an inner join
	    # also if we are NOTing, but we are looking for a tag *value*
	    if( ($op eq "AND") || ($hasvals && ($op eq "NOT")))
	    {
		# hack - if right child is a NOT, we don't need extra join/brackets
		# NOT will do the same and we will end up with an extra one
		unless($right && $right->name() && $right->name() eq "NOT")
		{
		    push(@joins, "INNER");
		    push(@outjoins, "INNER");
		}
	    }
	    elsif($op eq "NOT")
	    {
		$not=1;
		push(@joins, "LEFT");
		push(@outjoins, "LEFT");
#		print("LEFT: ", $left->print(), "\n") if ($left);
#		print("RIGHT: ", $right->print(), "\n") if($right);
	    }
	}
	my ($rightstr, @rightjoins) = $right->to_sql($hasvals, $not, @joins) if($right);
	push(@outjoins, @rightjoins);
#	print "LEFT (", scalar(@leftjoins), "): $leftstr\n";
#	print "RIGHT (", scalar(@rightjoins), "): $rightstr\n";
	my $str=$leftstr;
	$str .= " $op " if($op && !$not);
	$str .= $rightstr;
	if($op || ($left && $right))
	{
	    $str="(" . $str . ")";
	}
#	print "STR: $str\n";
	return($str, @outjoins);
    }
}

sub used_tags
{
    my($self)=@_;
    if($self->type() eq "boolean")
    {
	my @used=();
	push(@used, $self->left()->used_tags())  if($self->left());
	push(@used, $self->right()->used_tags()) if($self->right());
	return(grep { defined; } @used);
    }
    return $self->id();
}

1;
