package DBIx::Class::Helper::ResultSet::CrossTab;

# ABSTRACT: helper to simulate crosstab

use strict;
use warnings;

use parent 'DBIx::Class::ResultSet';

# use Data::Dump qw/dump/;

my $quote = sub { return "'" . $_[0] . "'" };

use DBIx::Class::Helper::ResultSet::CrossTab::Util;

sub crosstab {
    my $self  = shift;
    my $query = shift;
    my $opts  = shift;

    $DBIx::Class::Helper::ResultSet::CrossTab::Util::quote = sub { return $self->result_source->storage->dbh->quote( $_[0]) };

    my @values;

    # ---------------------------------------------------------------------
    # get the values for pivoting
    # ---------------------------------------------------------------------
    my $v = $self->search($query, { columns => $opts->{on}, group_by => $opts->{on}, order_by => $opts->{on} });
    $v->result_class('DBIx::Class::ResultClass::HashRefInflator');
    @values = map { my %h; @h{@{$opts->{on}}} = @{$_}{@{$opts->{on}}}; \%h; } $v->all;

    # ---------------------------------------------------------------------
    # create the pivot fields
    # ---------------------------------------------------------------------
    my @funcs = @{ $opts->{pivot}};
    my @cross_select = map { my $f = $_; map { my $r = summary_function_to_pivot_field_def($f, %{$_}); \$r } @values } @funcs;
    my @cross_as =     map { my $f = $_; map { my $r = summary_function_to_pivot_field_name($f, %{$_}); $r } @values } @funcs;

    my $re = quotemeta($opts->{on}->[0]);

    my (@as, @select);

    @select = grep { !/$re/ } @{$self->_resolved_attrs->{select}};
    @as     = @select;

    my $cross = $self
	->search($query, {
			  'select' => [ @select, @cross_select ],
			  'as'     => [ @as, @cross_as ],
			  group_by => $opts->{group_by}
			 });

    my ($sql, @bind) = @{${$cross->as_query}};
    return $cross;
}

1;

#################### pod generated by Pod::Autopod - keep this line to make pod updates possible ####################

=head1 NAME

DBIx::Class::Helper::ResultSet::CrossTab

=head1 DESCRIPTION

A helper to simulate crosstab functionality in DBIx::Class

=head1 REQUIRES

L<String::SQLColumnName> 

L<parent> 

=head1 METHODS

=head2 crosstab

     my $r = $s->search({}, { columns => [qw/fruit channel/], } )
               ->crosstab({}, { 
                                select  => [qw/fruit/], 
                                on => [qw/channel/], 
                                pivot => [ { sum => 'units' } ], 
                                group_by => [qw/fruit/] 
                              });

     $r->result_class('DBIx::Class::ResultClass::HashRefInflator');  # to inflate rows

=head2 ATTRIBUTES

=head3 pivot

This includes all functions and fields that must be summarized as an arrayref, in the same way as data can be aggregated in DBIx::Class.

these:

     pivot => [ { sum => 'units' } ]  # aggregate function and field
 
     pivot => [ \"sum(units)" ]       # literal SQL

will both provide a sum of units as the result of the crosstab, while this:

     pivot => [ \"sum(units)", { avg => 'units }, \"count(distinct country)"  ]  # mixed, multiple fields

will generate a total of units, the average and the number of distinct countries

=head3 on

This is the field (or function thereof) that will generate the column headers

     on => [qw/channel/]

will generate columns by channel

     on => [qw/channel country/]

will have one column per channel and country combination. It's not possible yet to pivot on a function of a field.

=head3 others

All other attributes work as documented in DBIx::Class::ResultSet

-head2 CHANGES

Version 0.006 adds functionality for nested summary functions and therefore breaks column naming.

=head2 COPYRIGHT AND LICENSE

Copyright 2016-2017 Simone Cesano

This library is free software; you may redistribute it and/or modify it under the same terms as Perl itself.

=head2 CREDITS

I owe the general idea to Giuseppe Maxia, and I re-used a couple of code snippets from Arthur Axel "fREW" Schmidt.

=cut

