##-*- Mode: CPerl -*-

##======================================================================
## top-level
package DDC::PP::CQFilter;
use DDC::PP::Object;
use DDC::PP::Constants;
use DDC::Utils qw();
use strict;

##======================================================================
## CQFilter
package DDC::PP::CQFilter;
use strict;
our @ISA = qw(DDC::PP::Object);

sub new {
  my ($that,%opts) = @_;
  return $that->SUPER::new(%opts);
}

sub toString { return "#FILTER[?]"; }

##======================================================================
package DDC::PP::CQFSort;
use strict;
our @ISA = qw(DDC::PP::CQFilter);

__PACKAGE__->defprop('Arg0');
__PACKAGE__->defprop('Arg1');
__PACKAGE__->defprop('Arg2');
__PACKAGE__->defprop('Type');

sub defaultSort { return 'NoSort'; }
sub new {
  my ($that,$sort,$arg0,$arg1,$arg2,%opts) = @_;
  return $that->SUPER::new(Type=>(defined($sort) ? $sort : $DDC::PP::HitSortEnum{$that->defaultSort}),
			   Arg0=>$arg0,
			   Arg1=>$arg1,
			   Arg2=>$arg2,
			   %opts);
}
sub new_i {
  my ($that,$sort,$arg0,$arg1,$arg2,%opts) = @_;
  return $that->new($sort,$arg0,($arg1+0),($arg2+0),%opts);
}

sub argString {
  return !defined($_[1]) || $_[1] eq '' ? '' : DDC::Utils::escapeq($_[1]);
}
sub argStringE {
  return DDC::Utils::escapeq(defined($_[1]) ? $_[1] : '');
}
sub toString {
  my $f = shift;
  my $args = join(',',
		  ($f->{Arg0} ? $f->{Arg0} : qw()),
		  ($f->{Arg1} || $f->{Arg2}
		   ? ((defined($f->{Arg1}) ? $f->{Arg1} : ''),
		      (defined($f->{Arg2}) ? $f->{Arg2} : ''))
		   : qw())
		 );
  return '#'.uc($DDC::PP::HitSortEnumStrings[$f->{Type}]).($args ? "[$args]" : '');
}

sub jsonType { return (FilterType=>$DDC::PP::HitSortEnumStrings[$_[0]{Type}]); }
sub jsonMinMax { return (Min=>$_[0]{Arg1}, Max=>$_[0]{Arg2}); }
sub jsonData { return ($_[0]->jsonType, $_[0]->jsonMinMax); }

##-- ddc-compatible hash-conversion (for toJson())
sub toHash {
  my ($obj,%opts) = @_;
  return $obj->SUPER::toHash(%opts) if (!$opts{json});
  return { class=>$obj->jsonClass, $obj->jsonData };
}

##-- pseudo-accessors (for json)
__PACKAGE__->defalias('Min'=>'Arg1', 0,1);
__PACKAGE__->defalias('Max'=>'Arg2', 0,1);
#sub getFilterType { return $DDC::PP::HitSortEnumStrings[$_[0]{Type}]; }
sub setFilterType { return $_[0]{Type} = $DDC::PP::HitSortEnum{$_[1]}; }


##======================================================================
## CQFRankSort
package DDC::PP::CQFRankSort;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

sub defaultSort { return 'GreaterByRank'; }
sub jsonMinMax { return qw(); }

##======================================================================
## CQFDateSort
package DDC::PP::CQFDateSort;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

sub defaultSort { return 'LessByDate' };
sub new {
  my ($that,$ftype,$lb,$ub,%opts) = @_;
  return $that->SUPER::new($ftype,'',$lb,$ub,%opts);
}

##======================================================================
## CQFSizeSort
package DDC::PP::CQFSizeSort;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

sub defaultSort { return 'LessBySize'; }
sub new {
  my ($that,$ftype,$lb,$ub,%opts) = @_;
  return $that->SUPER::new($ftype,'',$lb,$ub,%opts);
}

##======================================================================
## CQFRandomSort
package DDC::PP::CQFRandomSort;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

sub defaultSort { return 'RandomSort'; }
sub new {
  my ($that,$seed,%opts) = @_;
  return $that->SUPER::new(undef,'',$seed,'',%opts);
}
*new_i = \&new;

##-- ddc-json compat
__PACKAGE__->defalias('Seed'=>'Arg1', 0,1);
sub jsonMinMax { return (Seed=>$_[0]{Arg1}); }

##======================================================================
## CQFBiblSort
package DDC::PP::CQFBiblSort;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

sub defaultSort { return 'LessByFreeBiblField'; }
sub new {
  my ($that,$ftype,$field,$lb,$ub,%opts) = @_;
  return $that->SUPER::new($ftype,$field,$lb,$ub,%opts);
}

sub toString {
  my $f = shift;
  return ('#'.uc($DDC::PP::HitSortEnumStrings[$f->{Type}])
	  .'['.join(',',
		    $f->argString($f->{Arg0}),
		    (defined($f->{Arg1}) || defined($f->{Arg2})
		     ? ($f->argString($f->{Arg1}),$f->argString($f->{Arg2}))
		     : qw()),
		   )
	  .']');
}

##-- ddc-json compat
__PACKAGE__->defalias('Field'=>'Arg0', 0,1);
sub jsonData { return (Field=>$_[0]{Arg0}, $_[0]->SUPER::jsonData); }


##======================================================================
## CQFContextSort
package DDC::PP::CQFContextSort;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

sub defaultSort { return 'LessByMiddleContext'; }
sub new {
  my ($that,$ftype,$field,$matchid,$offset,$lb,$ub,%opts) = @_;
  return $that->SUPER::new($ftype,$field,$lb,$ub, MatchId=>($matchid||0), Offset=>($offset||0), %opts);
}

sub toString {
  my $f = shift;
  return ('#'.uc($DDC::PP::HitSortEnumStrings[$f->{Type}])
	  .'['.$f->argString($f->{Arg0})
	  .($f->{MatchId} ? " =$f->{MatchId}" : '')
	  .sprintf(" %+d", ($f->{Offset}||0))
	  .(defined($f->{Arg1}) || defined($f->{Arg2})
	    ? join(',', '', $f->argString($f->{Arg1}), $f->argString($f->{Arg2}))
	    : '')
	  .']');
}

sub jsonData { return (Field=>$_[0]{Arg0}, MatchId=>$_[0]{MatchId}, Offset=>$_[0]{Offset}, $_[0]->SUPER::jsonData); }
__PACKAGE__->defalias('Field'=>'Arg0', 0,1);

##======================================================================
## CQFHasField
package DDC::PP::CQFHasField;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

__PACKAGE__->defprop('Negated');
sub defaultSort { return 'NoSort'; }
sub new {
  my ($that,$field,$val,$negated,%opts) = @_;
  return $that->SUPER::new(undef,$field,$val,'',Negated=>($negated||0),%opts);
}

sub Negate { $_[0]{Negated} = $_[0]{Negated} ? 0 : 1; }

sub toString {
  my $f = shift;
  return (($f->{Negated} ? '!' : '')
	  ."#HAS[".$f->argStringE($f->{Arg0}).','.$f->valueString.']'
	 );
}
sub valueString { return $_[0]->argStringE($_[0]{Arg1}); }

sub jsonMinMax { return (Field=>$_[0]{Arg0}, Value=>$_[0]->jsonFieldValue, Negated=>($_[0]{Negated} ? 1 : 0)); }
sub jsonFieldValue { return $_[0]{Arg1}; }
__PACKAGE__->defalias('Field'=>'Arg0', 0,1);
__PACKAGE__->defalias('Value'=>'Arg1', 0,1);

##======================================================================
## CQFHasFieldValue
package DDC::PP::CQFHasFieldValue;
use strict;
our @ISA = qw(DDC::PP::CQFHasField);

##======================================================================
## CQFHasFieldRegex
package DDC::PP::CQFHasFieldRegex;
use strict;
our @ISA = qw(DDC::PP::CQFHasField);

__PACKAGE__->defprop('Regex');
sub new {
  my ($that,$field,$val,$negated,%opts) = @_;
  return $that->SUPER::new($field,$val,$negated,Regex=>$val,%opts);
}

sub valueString { return "/$_[0]{Regex}/"; }

##======================================================================
## CQFHasFieldPrefix
package DDC::PP::CQFHasFieldPrefix;
use strict;
our @ISA = qw(DDC::PP::CQFHasFieldRegex);

sub new {
  my ($that,$field,$val,$negated,%opts) = @_;
  return $that->SUPER::new($field,$val,$negated,Regex=>"^\\Q${val}\\E",%opts);
}

sub valueString { return $_[0]->argStringE($_[0]{Arg1}).'*'; }

##======================================================================
## CQFHasFieldSuffix
package DDC::PP::CQFHasFieldSuffix;
use strict;
our @ISA = qw(DDC::PP::CQFHasFieldRegex);

sub new {
  my ($that,$field,$val,$negated,%opts) = @_;
  return $that->SUPER::new($field,$val,$negated,Regex=>"\\Q${val}\\E\$",%opts);
}

sub valueString { return '*'.$_[0]->argStringE($_[0]{Arg1}); }

##======================================================================
## CQFHasFieldInfix
package DDC::PP::CQFHasFieldInfix;
use strict;
our @ISA = qw(DDC::PP::CQFHasFieldRegex);

sub new {
  my ($that,$field,$val,$negated,%opts) = @_;
  return $that->SUPER::new($field,$val,$negated,Regex=>"\\Q${val}\\E",%opts);
}

sub valueString { return '*'.$_[0]->argStringE($_[0]{Arg1}).'*'; }

##======================================================================
## CQFHasFieldSet
package DDC::PP::CQFHasFieldSet;
use strict;
our @ISA = qw(DDC::PP::CQFHasField);

__PACKAGE__->defprop('Values');
sub new {
  my ($that,$field,$vals,$negated,%opts) = @_;
  return $that->SUPER::new($field,"{}",$negated,Values=>($vals||[]),%opts);
}

sub SetValueString {
  my ($f,$vals) = @_;
  $vals ||= ($f->{Values}||[]);
  return join(',', map {$f->argStringE($_)} @$vals);
}
sub valueString { return '{' . $_[0]->SetValueString . '}'; }

sub jsonFieldValue { return $_[0]{Values}; }


##======================================================================
## CQFPrune

package DDC::PP::CQFPrune;
use strict;
our @ISA = qw(DDC::PP::CQFSort);

__PACKAGE__->defprop('Limit');
__PACKAGE__->defprop('Keys');
sub new {
  my ($that,$sort,$limit,$keys,%opts) = @_;
  return $that->SUPER::new($sort,undef,undef,undef,Limit=>($limit//0), Keys=>($keys//[]), %opts);
}

sub defaultSort { return 'LessByPruneKey'; }

sub toString {
  my $f = shift;
  my $s = ('#'.uc($DDC::PP::HitSortEnumStrings[$f->{Type}])
	   .'['.join(',',
		    ($f->{Limit}//0),
		    (UNIVERSAL::can($f->{Keys},'toString') ? $f->{Keys}->toString() : qw()),
		    )
	   .']');
}

sub jsonData {
  return ('Limit'=>($_[0]{Limit}//0), Keys=>$_[0]{Keys});
}


1; ##-- be happy

=pod

=head1 NAME

DDC::PP::CQFilter - pure-perl implementation of DDC::XS::CQFilter

=head1 SYNOPSIS

 use DDC::PP::CQFilter;
 #... stuff happens ...


=head1 DESCRIPTION

The DDC::PP::CQFilter class is a pure-perl fork of the L<DDC::XS::CQFilter|DDC::XS::CQFilter> class,
which see for details.

=head1 SEE ALSO

perl(1),
DDC::PP(3perl),
DDC::XS::CQFilter(3perl).

=head1 AUTHOR

Bryan Jurish E<lt>moocow@cpan.orgE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2016-2020 by Bryan Jurish

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.14.2 or,
at your option, any later version of Perl 5 you may have available.

=cut

