##-*- Mode: CPerl -*-

##======================================================================
## top-level
package DDC::PP::CQCount;
use DDC::PP::Constants;
use DDC::PP::CQuery;
use Carp qw(carp confess);
use strict;

##======================================================================
## CQCountKeyExpr
package DDC::PP::CQCountKeyExpr;
use strict;
our @ISA = qw(DDC::PP::CQuery);

sub CanCountByFile { return 1; }

##======================================================================
## CQCountKeyExprConstant
package DDC::PP::CQCountKeyExprConstant;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExpr);

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

sub toString { return '@'.$_[0]->sqString($_[0]{Label}); }

##======================================================================
## CQCountKeyExprMeta
package DDC::PP::CQCountKeyExprMeta;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExpr);

##======================================================================
## CQCountKeyExprFileId
package DDC::PP::CQCountKeyExprFileId;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprMeta);

sub defaultLabel { return 'fileid'; }

##======================================================================
## CQCountKeyExprIndexed
package DDC::PP::CQCountKeyExprIndexed;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprMeta);

sub defaultLabel { return 'file'; }

##======================================================================
## CQCountKeyExprFileName
package DDC::PP::CQCountKeyExprFileName;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprIndexed);

sub defaultLabel { return 'filename'; }

##======================================================================
## CQCountKeyExprDate
package DDC::PP::CQCountKeyExprDate;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprIndexed);

sub defaultLabel { return 'date'; }

##======================================================================
## CQCountKeyExprDateSlice
package DDC::PP::CQCountKeyExprDateSlice;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprDate);

__PACKAGE__->defprop('Slice');
sub new {
  my ($that,$label,$slice,%opts) = @_;
  return $that->SUPER::new($label,Slice=>$slice,%opts); ##-- lower-case 'slice' in DDC, should be ok
}

sub toString { return $_[0]->sqString($_[0]{Label}).'/'.($_[0]{Slice}||1); }

##======================================================================
## CQCountKeyExprBibl
package DDC::PP::CQCountKeyExprBibl;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprIndexed);

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

sub toString { return $_[0]->sqString($_[0]{Label}); }

##======================================================================
## CQCountKeyExprRegex
package DDC::PP::CQCountKeyExprRegex;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprIndexed);

__PACKAGE__->defprop('Src');
__PACKAGE__->defprop('Pattern');
__PACKAGE__->defprop('Replacement');
__PACKAGE__->defprop('Modifiers');
__PACKAGE__->defprop('isGlobal');
sub defaultLabel { return 'regex'; }
sub new {
  my ($that,$src,$pat,$repl,$mods,%opts) = @_;
  return $that->SUPER::new(undef,Src=>$src,Pattern=>$pat,Replacement=>$repl,Modifiers=>$mods,IsGlobal=>0,%opts);
}

sub Children { [grep {defined($_)} $_[0]{Src}]; }

sub Clear { delete $_[0]{Src}; }
sub toString {
  return '(' . $_[0]{Src}->toString . " ~ s/$_[0]{Pattern}/$_[0]{Replacement}/$_[0]{Modifiers})";
}

##======================================================================
## CQCountKeyExprToken
package DDC::PP::CQCountKeyExprToken;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExprIndexed);

__PACKAGE__->defprop('IndexName');
__PACKAGE__->defprop('MatchId');
__PACKAGE__->defprop('Offset');
sub defaultLabel { return 'token'; }
sub new {
  my ($that,$index,$matchid,$offset,%opts) = @_;
  return $that->SUPER::new(undef,IndexName=>($index||"Token"),MatchId=>($matchid||0),Offset=>($offset||0),%opts);
}

sub CanCountByFile { return 0; }
*GetMatchId = \&getMatchId;
*SetMatchId = \&setMatchId;

sub toString {
  return ('$'
	  .$_[0]{IndexName}
	  .($_[0]{MatchId} ? sprintf(" =%hhu", $_[0]{MatchId}) : '')
	  .($_[0]{Offset}  ? sprintf(" %+d", $_[0]{Offset}) : '')
	 );
}

##======================================================================
## CQCountKeyExprList
package DDC::PP::CQCountKeyExprList;
use strict;
our @ISA = qw(DDC::PP::CQCountKeyExpr);

__PACKAGE__->defprop('Exprs');
sub defaultLabel { return 'list'; }
sub new {
  my $that = shift;
  return $that->SUPER::new(undef,Exprs=>[],@_);
}


sub Clear { @{$_[0]{Exprs}} = qw(); }
sub empty { return !$_[0]{Exprs} || !@{$_[0]{Exprs}}; }
sub PushKey { push(@{$_[0]{Exprs}},$_[1]); }

sub CanCountByFile { return !grep {$_ && !$_->CanCountByFile} @{$_[0]{Exprs}||[]}; }
sub GetMatchId {
  my ($id);
  foreach (@{$_[0]{Exprs}||[]}) {
    return $id if ($_ && ($id=$_->GetMatchId));
  }
  return 0;
}
#sub SetMatchId  ##-- not implemented

sub Children { return $_[0]{Exprs} || []; }

sub toString {
  return join(',', map {$_->toString} @{$_[0]{Exprs}||[]});
}

##======================================================================
## CQCount
package DDC::PP::CQCount;
use strict;
our @ISA = qw(DDC::PP::CQuery);

__PACKAGE__->defprop('Dtr');
__PACKAGE__->defprop('Sample');
__PACKAGE__->defprop('Sort');
__PACKAGE__->defprop('Lo');
__PACKAGE__->defprop('Hi');
__PACKAGE__->defprop('Keys');
sub new {
  my ($that,$dtr,$keys,$samp,$sort,$lo,$hi,%opts) = @_;
  return $that->SUPER::new('COUNT',Dtr=>$dtr,Keys=>$keys,Sample=>($samp||-1),Sort=>($sort||DDC::PP::NoSort),Lo=>$lo,Hi=>$hi,%opts);
}

sub Children { [grep {defined($_)} @{$_[0]}{qw(Dtr Keys)}]; }

sub Clear { delete @{$_[0]}{qw(Dtr Keys)}; }
sub GetMatchId {
  return (($_[0]{Keys} && $_[0]{Keys}->GetMatchId)
	  || ($_[0]{Dtr} && $_[0]{Dtr}->GetMatchId)
	  || 0);
}

sub toString {
  return "COUNT(" . $_[0]{Dtr}->toString . $_[0]{Dtr}->optionsToString .")".  $_[0]->countOptionsToString;
}
sub countOptionsToString {
  my $obj = shift;
  return (
	  ($obj->{Keys} && !$obj->{Keys}->empty ? (" #BY[".$obj->{Keys}->toString."]") : '')
	  .($obj->{Sample} && $obj->{Sample} > 0 ? " #SAMPLE $obj->{Sample}" : '')
	  .($obj->{Sort} != $DDC::PP::HitSortEnum{NoSort}
	    ? (" #".uc($DDC::PP::HitSortEnumStrings[$obj->{Sort}])
	       .($obj->{Lo} || $obj->{Hi}
		 ? ("[".($obj->{Lo} ? $obj->sqString($obj->{Lo}) : '')
		    .",".($obj->{Hi} ? $obj->sqString($obj->{Hi}) : '')
		    ."]")
		 : '')
	      )
	    : '')
	 );
}

##======================================================================
## CQKeys
package DDC::PP::CQKeys;
use strict;
our @ISA = qw(DDC::PP::CQuery);

__PACKAGE__->defprop('QCount');
__PACKAGE__->defprop('CountLimit');
__PACKAGE__->defprop('IndexNames');
__PACKAGE__->defprop('MatchId');
sub new {
  my ($that,$qcount,$climit,$ixnames,%opts) = @_;
  return $that->SUPER::new('KEYS',QCount=>$qcount,CountLimit=>($climit||-1),IndexNames=>($ixnames||[]),%opts);
}

sub GetMatchId {
  return ($_[0]{MatchId}
	  || ($_[0]{QCount} && $_[0]{QCount}->GetMatchId)
	  || 0);
}
*SetMatchId = \&setMatchId;

sub toString {
  my $obj = shift;
  return (
	  ($obj->{IndexNames} && @{$obj->{IndexNames}}
	   ? ('$('.join(',', map {$obj->sqString($_)} @{$obj->{IndexNames}}).')=')
	   : '')
	  .'KEYS('
	  .($obj->{QCount}
	    ? (($obj->{QCount}{Dtr} ? ($obj->{QCount}{Dtr}->toString.$obj->{QCount}{Dtr}->optionsToString) : '')
	       .$obj->{QCount}->countOptionsToString)
	    : '')
	  .($obj->{CountLimit} > 0 ? " #CLIMIT $obj->{CountLimit}" : '')
	  .')'
	  .($obj->{MatchId} ? " =$obj->{MatchId}" : '')
	 );
}



1; ##-- be happy

=pod

=head1 NAME

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

=head1 SYNOPSIS

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


=head1 DESCRIPTION

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

=head1 SEE ALSO

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

=head1 AUTHOR

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

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2016 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