The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package # hide from PAUSE
App::DBBrowser::Table::ColFunc;

use warnings;
use strict;
use 5.008003;
no warnings 'utf8';

our $VERSION = '1.056';

use Clone           qw( clone );
use List::MoreUtils qw( first_index );
use Term::Choose    qw();

use App::DBBrowser::Auxil;
use App::DBBrowser::DB;


sub new {
    my ( $class, $info, $opt ) = @_;
    bless { info => $info, opt => $opt }, $class;
}


sub __col_function {
    my ( $self, $dbh, $sql, $backup_sql, $qt_columns, $pr_columns, $sql_type ) = @_;
    my $auxil  = App::DBBrowser::Auxil->new( $self->{info} );
    my $stmt_h = Term::Choose->new( $self->{info}{lyt_stmt_h} );
    my @functions = @{$self->{info}{scalar_func_keys}};
    my $stmt_key = '';
    if ( $sql->{select_type} eq '*' ) {
        @{$sql->{quote}{chosen_cols}} = map { $qt_columns->{$_} } @$pr_columns;
        @{$sql->{print}{chosen_cols}} = @$pr_columns;
        $stmt_key = 'chosen_cols';
    }
    elsif ( $sql->{select_type} eq 'chosen_cols' ) {
        $stmt_key = 'chosen_cols';
    }
    if ( $stmt_key eq 'chosen_cols' ) {
        if ( ! $sql->{scalar_func_backup_pr_col}{chosen_cols} ) {
            @{$sql->{scalar_func_backup_pr_col}{'chosen_cols'}} = @{$sql->{print}{chosen_cols}};
        }
    }
    else {
        if ( @{$sql->{print}{aggr_cols}} && ! $sql->{scalar_func_backup_pr_col}{aggr_cols} ) {
            @{$sql->{scalar_func_backup_pr_col}{'aggr_cols'}} = @{$sql->{print}{aggr_cols}};
        }
        if ( @{$sql->{print}{group_by_cols}} && ! $sql->{scalar_func_backup_pr_col}{group_by_cols} ) {
            @{$sql->{scalar_func_backup_pr_col}{'group_by_cols'}} = @{$sql->{print}{group_by_cols}};
        }
    }
    my $changed = 0;

    COL_SCALAR_FUNC: while ( 1 ) {
        my $default = 0;
        my $hidden_2 = 'Your choice:';
        my @pre = ( undef, $self->{info}{_confirm} );
        my $prompt = 'Choose:'; #
        if ( $sql_type eq 'Select' ) {
            unshift @pre, $hidden_2 if ! defined $pre[0] || $pre[0] ne $hidden_2;
            $prompt = '';
            $default = 1;
        }
        my @cols = $stmt_key eq 'chosen_cols'
            ? ( @{$sql->{print}{chosen_cols}} )
            : ( @{$sql->{print}{aggr_cols}}, @{$sql->{print}{group_by_cols}} );
        my $choices = [ @pre, map( "- $_", @cols ) ];
        $auxil->__print_sql_statement( $sql, $sql_type );
        # Choose
        my $idx = $stmt_h->choose(
            $choices,
            { %{$self->{info}{lyt_stmt_v}}, index => 1, default => $default, prompt => $prompt }
        );
        if ( ! defined $idx || ! defined $choices->[$idx] ) {
            $sql = clone( $backup_sql );
            return;
        }
        if ( $choices->[$idx] eq $hidden_2 ) { # prompt scalar-func-menu
            my @sql_types;
            if ( ! $self->{info}{multi_tbl} ) {
                @sql_types = ( 'Insert', 'Update', 'Delete' );
            }
            elsif ( $self->{info}{multi_tbl} eq 'join' && $self->{info}{db_driver} eq 'mysql' ) {
                @sql_types = ( 'Update' );
            }
            else {
                @sql_types = ();
            }
            if ( ! @sql_types ) {
                next COL_SCALAR_FUNC;
            }
            my $ch_types = [ undef, map( "- $_", @sql_types ) ];
            # Choose
            my $type_choice = $stmt_h->choose(
                $ch_types,
                { %{$self->{info}{lyt_stmt_v}}, prompt => 'Choose SQL type:', default => 0, clear_screen => 1 }
            );
            if ( defined $type_choice ) {
                ( $sql_type = $type_choice ) =~ s/^-\ //;
                $auxil->__reset_sql( $sql );
                return $sql_type;
            }
            return;
        }
        if ( $choices->[$idx] eq $self->{info}{_confirm} ) {
            if ( ! $changed ) {
                $sql = clone( $backup_sql );
                return;
            }
            $sql->{select_type} = $stmt_key if $sql->{select_type} eq '*';
            return $qt_columns, $pr_columns;
        }
        ( my $print_col = $choices->[$idx] ) =~ s/^\-\s//;
        $idx -= @pre;
        if ( $stmt_key ne 'chosen_cols' ) {
            if ( $idx - @{$sql->{print}{aggr_cols}} >= 0 ) {
                $idx -= @{$sql->{print}{aggr_cols}};
                $stmt_key = 'group_by_cols';
            }
            else {
                $stmt_key = 'aggr_cols';
            }
        }
        if ( $sql->{print}{$stmt_key}[$idx] ne $sql->{scalar_func_backup_pr_col}{$stmt_key}[$idx] ) {
            if ( $stmt_key ne 'aggr_cols' ) {
                my $i = first_index { $sql->{print}{$stmt_key}[$idx] eq $_ } @{$sql->{pr_col_with_scalar_func}};
                splice( @{$sql->{pr_col_with_scalar_func}}, $i, 1 );
            }
            $sql->{print}{$stmt_key}[$idx] = $sql->{scalar_func_backup_pr_col}{$stmt_key}[$idx];
            $sql->{quote}{$stmt_key}[$idx] = $qt_columns->{$sql->{scalar_func_backup_pr_col}{$stmt_key}[$idx]};
            $changed++;
            next COL_SCALAR_FUNC;
        }
        $auxil->__print_sql_statement( $sql, $sql_type );
        # Choose
        my $function = $stmt_h->choose(
            [ undef, map( "  $_", @functions ) ],
            { %{$self->{info}{lyt_stmt_v}} }
        );
        if ( ! defined $function ) {
            next COL_SCALAR_FUNC;
        }
        $function =~ s/^\s\s//;
        ( my $quote_col = $qt_columns->{$print_col} ) =~ s/\sAS\s\S+\z//;
        $auxil->__print_sql_statement( $sql, $sql_type );
        my ( $qt_scalar_func, $pr_scalar_func ) = $self->__prepare_col_func( $function, $quote_col, $print_col );
        if ( ! defined $qt_scalar_func ) {
            next COL_SCALAR_FUNC;
        }
        $pr_scalar_func = $auxil->__unambiguous_key( $pr_scalar_func, $pr_columns );
        if ( $stmt_key eq 'group_by_cols' ) {
            $sql->{quote}{$stmt_key}[$idx] = $qt_scalar_func;
            $sql->{print}{$stmt_key}[$idx] = $pr_scalar_func;
            $sql->{quote}{group_by_stmt} = " GROUP BY " . join( ', ', @{$sql->{quote}{$stmt_key}} );
            $sql->{print}{group_by_stmt} = " GROUP BY " . join( ', ', @{$sql->{print}{$stmt_key}} );
        }
        $sql->{quote}{$stmt_key}[$idx] = $qt_scalar_func;
        # alias to get a shorter scalar funtion column name in the tableprint (optional):
        $sql->{quote}{$stmt_key}[$idx] .= ' AS ' . $dbh->quote_identifier( $pr_scalar_func );
        $sql->{print}{$stmt_key}[$idx] = $pr_scalar_func;
        $qt_columns->{$pr_scalar_func} = $qt_scalar_func;
        if ( $stmt_key ne 'aggr_cols' ) { # aggregate functions are not allowed in WHERE clauses
            push @{$sql->{pr_col_with_scalar_func}}, $pr_scalar_func;
        }
        $changed++;
        next COL_SCALAR_FUNC;
    }
}


sub __prepare_col_func {
    my ( $self, $func, $quote_col, $print_col ) = @_;
    my $obj_db = App::DBBrowser::DB->new( $self->{info}, $self->{opt} );
    my $obj_ch = Term::Choose->new();
    my ( $quote_f, $print_f );
    $print_f = $self->{info}{scalar_func_h}{$func} . '(' . $print_col . ')';
    if ( $func =~ /^Epoch_to_Date(?:Time)?\z/ ) {
        my $prompt = "$print_f\nInterval:";
        my ( $microseconds, $milliseconds, $seconds ) = (
            '  ****************   Micro-Second',
            '  *************      Milli-Second',
            '  **********               Second' );
        my $choices = [ undef, $microseconds, $milliseconds, $seconds ];
        # Choose
        my $interval = $obj_ch->choose(
            $choices,
            { %{$self->{info}{lyt_stmt_v}}, prompt => $prompt }
        );
        return if ! defined $interval;
        my $div = $interval eq $microseconds ? 1000000 :
                  $interval eq $milliseconds ? 1000 : 1;
        if ( $func eq 'Epoch_to_DateTime' ) {
            $quote_f = $obj_db->epoch_to_datetime( $quote_col, $div );
        }
        else {
            $quote_f = $obj_db->epoch_to_date( $quote_col, $div );
        }
    }
    elsif ( $func eq 'Truncate' ) {
        my $prompt = "TRUNC $print_col\nDecimal places:";
        my $choices = [ undef, 0 .. 9 ];
        # Choose
        my $precision = $obj_ch->choose(
            $choices,
            { %{$self->{info}{lyt_stmt_h}}, prompt => $prompt }
        );
        return if ! defined $precision;
        $quote_f = $obj_db->truncate( $quote_col, $precision );
    }
    elsif ( $func eq 'Bit_Length' ) {
        $quote_f = $obj_db->bit_length( $quote_col );
    }
    elsif ( $func eq 'Char_Length' ) {
        $quote_f = $obj_db->char_length( $quote_col );
    }
    return $quote_f, $print_f;
}





1;


__END__