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::OptDB;

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

our $VERSION = '2.006';

use File::Basename qw( basename );

use Term::Choose       qw( choose );
use Term::Choose::Util qw( choose_a_subset settings_menu );
use Term::Form         qw();

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


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


sub connect_parameter {
    my ( $sf, $db_o, $db ) = @_;
    my $obj_db = App::DBBrowser::DB->new( $sf->{i}, $sf->{o} );
    my $plugin = $sf->{i}{plugin};
    my $cp = {
#        use_env_var => {},
#        required    => {},
#        secret      => {},
#        arguments   => {},
#        attributes  => {},
    };

    my $env_vars = $obj_db->env_variables();
    for my $env_var ( @$env_vars ) {
        if ( ! defined $db || ! defined $db_o->{$db}{$env_var} ) {
            # use global
            $cp->{use_env_var}{$env_var} = $db_o->{$plugin}{$env_var};
        }
        else {
            $cp->{use_env_var}{$env_var} = $db_o->{$db}{$env_var};
        }
    }

    my $attrs = $obj_db->set_attributes();
    # attributes added by hand to the config file: attribues are
    # only used if they have entries in the set_attributes method
    for my $attr ( @$attrs ) {
        my $name = $attr->{name};
        if ( ! defined $db || ! defined $db_o->{$db}{$name} ) {
            if ( ! defined $db_o->{$plugin}{$name} ) {
                # set global to default
                $db_o->{$plugin}{$name} = $attr->{values}[$attr->{default}];
            }
            # use global
            $cp->{attributes}{$name} = $db_o->{$plugin}{$name};
        }
        else {
            $cp->{attributes}{$name} = $db_o->{$db}{$name};
        }
    }

    my $arg = $obj_db->read_arguments();
    for my $item ( @$arg ) {
        my $name = $item->{name};
        my $required = 'field_' . $name;
        $cp->{secret}{$name} = $item->{secret};
        if ( ! defined $db || ! defined $db_o->{$db}{$required} ) {
            if ( ! defined $db_o->{$plugin}{$required} ) {
                # set global to default
                $db_o->{$plugin}{$required} = 1; # All fields required by default
            }
            # use gloabl
            $cp->{required}{$name} = $db_o->{$plugin}{$required};
        }
        else {
            $cp->{required}{$name} = $db_o->{$db}{$required};
        }
        # if a login error occurred, the next time the user has to enter the arguments by hand
        if ( ! $sf->{i}{login_error} ) {
            if ( ! defined $db || ! defined $db_o->{$db}{$name} ) {
                # use global
                $cp->{arguments}{$name} = $db_o->{$plugin}{$name};
            }
            else {
                $cp->{arguments}{$name} = $db_o->{$db}{$name};
            }
        }
    }
    if ( exists $sf->{i}{login_error} ) {
        delete $sf->{i}{login_error};
    }
    return $cp;
}



sub database_setting {
    my ( $sf, $db ) = @_;
    my $old_idx_sec = 0;

    SECTION: while ( 1 ) {
        my ( $plugin, $section );
        #my ( $driver, $plugin, $section );
        if ( defined $db ) {
            $plugin = $sf->{i}{plugin};
            #$driver = $sf->{i}{driver};
            $section = $db;
        }
        else {
            if ( @{$sf->{o}{G}{plugins}} == 1 ) {
                $plugin = $sf->{o}{G}{plugins}[0];
            }
            else {
                $ENV{TC_RESET_AUTO_UP} = 0;
                my $choices = [ undef, map( "- $_", @{$sf->{o}{G}{plugins}} ) ];
                # Choose
                my $idx_sec = choose(
                    $choices,
                    { %{$sf->{i}{lyt_3}}, undef => $sf->{i}{back_config}, default => $old_idx_sec, index => 1 }
                );
                if ( ! defined $idx_sec || ! defined $choices->[$idx_sec] ) {
                    return;
                }
                if ( $sf->{o}{G}{menu_memory} ) {
                    if ( $old_idx_sec == $idx_sec && ! $ENV{TC_RESET_AUTO_UP} ) {
                        $old_idx_sec = 0;
                        next SECTION;
                    }
                    else {
                        $old_idx_sec = $idx_sec;
                    }
                }
                delete $ENV{TC_RESET_AUTO_UP};
                $plugin = $choices->[$idx_sec];
                $plugin =~ s/^-\ //;
            }
            $plugin = 'App::DBBrowser::DB::' . $plugin;
            $sf->{i}{plugin} = $plugin;
            $section = $plugin;
        }
        my $obj_db = App::DBBrowser::DB->new( $sf->{i}, $sf->{o} );
        #$driver = $obj_db->driver() if ! $driver;
        my $env_var    = $obj_db->env_variables();
        my $login_data = $obj_db->read_arguments();
        my $attr       = $obj_db->set_attributes();
        my $items = {
            required => [ map { {
                    name         => 'field_' . $_->{name},
                    prompt       => exists $_->{prompt} ? $_->{prompt} : $_->{name},
                    values => [ 'NO', 'YES' ]
                } } @$login_data ],
            env_variables => [ map { { #
                    name         => $_,
                    prompt       => $_,
                    values => [ 'NO', 'YES' ]
                } } @$env_var ],
            arguments => [
                    grep { ! $_->{secret} } @$login_data
                ],
            attributes => $attr,
        };
        my @groups;
        push @groups, [ 'required',      "- Fields"             ] if @{$items->{required}};
        push @groups, [ 'env_variables', "- ENV Variables"      ] if @{$items->{env_variables}};
        push @groups, [ 'arguments',     "- Login Data"         ] if @{$items->{arguments}};
        push @groups, [ 'attributes',    "- Attributes"         ] if @{$items->{attributes}};
        my $prompt = defined $db ? 'DB: "' . $db . '"' : '"' . $plugin . '"';
        my $db_o = $sf->__read_db_config_files();

        my $changed = 0;
        my $old_idx_group = 0;

        GROUP: while ( 1 ) {
            my $reset = '  Reset DB';
            my @pre = ( undef );
            my $choices = [ @pre, map( $_->[1], @groups ) ];
            push @$choices, $reset if ! defined $db;
            # Choose
            $ENV{TC_RESET_AUTO_UP} = 0;
            my $idx_group = choose(
                $choices,
                { %{$sf->{i}{lyt_3}}, prompt => $prompt, index => 1,
                  default => $old_idx_group, undef => $sf->{i}{back_config} }
            );
            if ( ! defined $idx_group || ! defined $choices->[$idx_group] ) {
                if ( $sf->{i}{write_config} ) {
                    $sf->__write_db_config_files( $db_o );
                    delete $sf->{i}{write_config};
                    $changed++;
                }
                next SECTION if ! $db && @{$sf->{o}{G}{plugins}} > 1;
                return $changed;
            }
            if ( $sf->{o}{G}{menu_memory} ) {
                if ( $old_idx_group == $idx_group && ! $ENV{TC_RESET_AUTO_UP} ) {
                    $old_idx_group = 0;
                    next GROUP;
                }
                else {
                    $old_idx_group = $idx_group;
                }
            }
            delete $ENV{TC_RESET_AUTO_UP};
            if ( $choices->[$idx_group] eq $reset ) {
                my @databases;
                for my $section ( keys %$db_o ) {
                    push @databases, $section if $section ne $plugin;
                }
                if ( ! @databases ) {
                    choose(
                        [ 'No databases with customized settings.' ],
                        { %{$sf->{i}{lyt_m}}, prompt => 'Press ENTER' }
                    );
                    next GROUP;
                }
                my $choices = choose_a_subset(
                    [ sort @databases ],
                    { p_new => 'Reset DB: ', mouse => $sf->{o}{table}{mouse} }
                );
                if ( ! $choices->[0] ) {
                    next GROUP;
                }
                for my $db ( @$choices ) {
                    delete $db_o->{$db};
                }
                $sf->{i}{write_config}++;
                next GROUP;;
            }
            my $group  = $groups[$idx_group-@pre][0];
            if ( $group eq 'required' ) {
                my $sub_menu = [];
                for my $item ( @{$items->{$group}} ) {
                    my $required = $item->{name};
                    push @$sub_menu, [ $required, '- ' . $item->{prompt}, $item->{values} ];
                    if ( ! defined $db_o->{$section}{$required} ) {
                        if ( defined $db_o->{$plugin}{$required} ) {
                            # set to global (if $section == $db )
                            $db_o->{$section}{$required} = $db_o->{$plugin}{$required};
                        }
                        else {
                            # set to default
                            $db_o->{$section}{$required} = 1;  # All fields required by default
                        }
                    }
                }
                my $prompt = 'Required fields (' . $plugin . '):';
                $sf->__settings_menu_wrap_db( $db_o, $section, $sub_menu, $prompt );
                next GROUP;
            }
            elsif ( $group eq 'env_variables' ) {
                my $sub_menu = [];
                for my $item ( @{$items->{$group}} ) {
                    my $env_variable = $item->{name};
                    push @$sub_menu, [ $env_variable, '- ' . $item->{prompt}, $item->{values} ];
                    if ( ! defined $db_o->{$section}{$env_variable} ) {
                        if ( defined $db_o->{$plugin}{$env_variable} ) {
                            # set to global
                            $db_o->{$section}{$env_variable} = $db_o->{$plugin}{$env_variable};
                        }
                        else {
                            # set to default
                            $db_o->{$section}{$env_variable} = 0;
                        }
                    }
                }
                my $prompt = 'Use ENV variables (' . $plugin . '):';
                $sf->__settings_menu_wrap_db( $db_o, $section, $sub_menu, $prompt );
                next GROUP;
            }
            elsif ( $group eq 'arguments' ) {
               for my $item ( @{$items->{$group}} ) {
                    my $opt = $item->{name};
                    if ( ! defined $db_o->{$section}{$opt} ) {
                        if ( defined $db_o->{$plugin}{$opt} ) {
                            # set to global
                            $db_o->{$section}{$opt} = $db_o->{$plugin}{$opt};
                        }
                    }
                }
                my $prompt = 'Default login data (' . $plugin . '):';
                $sf->__group_readline_db( $db_o, $section, $items->{$group}, $prompt );
            }
            elsif ( $group eq 'attributes' ) {
                my $sub_menu = [];
                for my $item ( @{$items->{$group}} ) {
                    my $opt = $item->{name};
                    my $prompt = '- ' . ( exists $item->{prompt} ? $item->{prompt} : $item->{name} );
                    push @$sub_menu, [ $opt, $prompt, $item->{values} ];
                    if ( ! defined $db_o->{$section}{$opt} ) {
                        if ( defined $db_o->{$plugin}{$opt} ) {
                            # set to global
                            $db_o->{$section}{$opt} = $db_o->{$plugin}{$opt};
                        }
                        else {
                            # set to default
                            $db_o->{$section}{$opt} = $item->{values}[$item->{default}];
                        }
                    }
                }
                my $prompt = 'Options (' . $plugin . '):';
                $sf->__settings_menu_wrap_db( $db_o, $section, $sub_menu, $prompt );
                next GROUP;
            }
        }
    }
}


sub __settings_menu_wrap_db {
    my ( $sf, $db_o, $section, $sub_menu, $prompt ) = @_;
    my $changed = settings_menu( $sub_menu, $db_o->{$section}, { prompt => $prompt, mouse => $sf->{o}{table}{mouse} } );
    return if ! $changed;
    $sf->{i}{write_config}++;
}

sub __group_readline_db {
    my ( $sf, $db_o, $section, $items, $prompt ) = @_;
    my $list = [ map {
        [
            exists $_->{prompt} ? $_->{prompt} : $_->{name},
            $db_o->{$section}{$_->{name}}
        ]
    } @{$items} ];
    my $trs = Term::Form->new();
    my $new_list = $trs->fill_form(
        $list,
        { prompt => $prompt, auto_up => 2, confirm => $sf->{i}{_confirm}, back => $sf->{i}{_back} }
    );
    if ( $new_list ) {
        for my $i ( 0 .. $#$items ) {
            $db_o->{$section}{$items->[$i]{name}} = $new_list->[$i][1];
        }
        $sf->{i}{write_config}++;
    }
}


sub __write_db_config_files {
    my ( $sf, $db_o ) = @_;
    my $ax = App::DBBrowser::Auxil->new( $sf->{i}, $sf->{o} );
    my $plugin = $sf->{i}{plugin};
    $plugin=~ s/^App::DBBrowser::DB:://;
    my $file_name = sprintf( $sf->{i}{conf_file_fmt}, $plugin );
    $file_name=~ s/^App::DBBrowser::DB:://;
    if ( defined $db_o && %$db_o ) {
        $ax->write_json( $file_name, $db_o );
    }
}


sub __read_db_config_files {
    my ( $sf ) = @_;
    my $ax = App::DBBrowser::Auxil->new( $sf->{i}, $sf->{o} );
    my $plugin = $sf->{i}{plugin};
    $plugin=~ s/^App::DBBrowser::DB:://;
    my $file_name = sprintf( $sf->{i}{conf_file_fmt}, $plugin );
    my $db_o;
    if ( -f $file_name && -s $file_name ) {
        $db_o = $ax->read_json( $file_name ) || {};
    }
    return $db_o;
}




1;


__END__