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

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

our $VERSION = '1.018';

use File::Basename        qw( basename fileparse );
use File::Spec::Functions qw( catfile );
use FindBin               qw( $RealBin $RealScript );
#use Pod::Usage            qw( pod2usage );  # "require"-d

use Term::Choose           qw( choose );
use Term::Choose::Util     qw( insert_sep print_hash choose_a_number choose_a_subset choose_multi choose_dirs choose_a_dir );
use Term::ReadLine::Simple qw();

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



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


sub defaults {
    my ( $self, $section, $key ) = @_;
    my $defaults = {
        G => {
            db_plugins           => [ 'SQLite', 'mysql', 'Pg' ],
            menus_config_memory  => 0,
            menu_sql_memory      => 0,
            menus_db_memory      => 0,
            thsd_sep             => ',',
            metadata             => 0,
            lock_stmt            => 0,
            operators            => [ "REGEXP", "REGEXP_i", " = ", " != ", " < ", " > ", "IS NULL", "IS NOT NULL" ],
            parentheses_w        => 0,
            parentheses_h        => 0,
        },
        table => {
            table_expand         => 1,
            mouse                => 0,
            max_rows             => 50_000,
            keep_header          => 0,
            progress_bar         => 40_000,
            min_col_width        => 30,
            tab_width            => 2,
            undef                => '',
            binary_string        => 'BNRY',
            binary_filter        => 0,
        },
        insert => {
        # Input
            input_modes          => [ 'Cols', 'Rows', 'Multi-row', 'File' ],
            files_dir            => undef,
            file_encoding        => 'UTF-8',
            max_files            => 15,
        # Parsing
            parse_mode           => 0,
            # Text::CSV:
            sep_char             => ',',
            quote_char           => '"',
            escape_char          => '"',
            allow_loose_escapes  => 0,
            allow_loose_quotes   => 0,
            allow_whitespace     => 0,
            auto_diag            => 1,
            blank_is_undef       => 1,
            binary               => 1,
            empty_is_undef       => 0,
            # split:
            i_r_s                => '\n',
            i_f_s                => ',',
        # create table defaults:
            id_col_name          => 'ID_a',
            default_data_type    => 'TEXT',
        }
    };
    return $defaults                   if ! $section;
    return $defaults->{$section}       if ! $key;
    return $defaults->{$section}{$key};
}


sub __sub_menus_insert {
    my ( $self, $group ) = @_;
    my $sub_menus_insert = {
        main_insert => [
            { name => 'input_modes',           text => "- Read",          section => 'insert' },
            { name => 'files_dir',             text => "- File Dir",      section => 'insert' },
            { name => 'file_encoding',         text => "- File Encoding", section => 'insert' },
            { name => 'max_files',             text => "- File History",  section => 'insert' },
            { name => 'parse_mode',            text => "- Parse-mode",    section => 'insert' },
            { name => '_module_Text_CSV',      text => "- conf T::CSV",   section => 'insert' },
            { name => '_parse_with_split',     text => "- conf 'split'",  section => 'insert' },
            { name => 'create_table_defaults', text => "- Create-table",  section => 'insert' },
        ],
        _module_Text_CSV => [
            { name => '_csv_char',    text => "- *_char attributes", section => 'insert' },
            { name => '_options_csv', text => "-  Other attributes", section => 'insert' },
        ],
    };
    return $sub_menus_insert->{$group};
}


sub __config_insert {
    my ( $self ) = @_;
    my $old_idx = 0;
    my $backup_old_idx = 0;
    my $group  = 'main_insert';

    GROUP_INSERT: while ( 1 ) {
        my $sub_menu_insert = $self->__sub_menus_insert( $group );

        OPTION_INSERT: while ( 1 ) {
            my $prompt;
            if ( $group =~ /^_module_(.+)\z/ ) {
                ( my $name = $1 ) =~ s/_/::/g;
                $prompt = '"' . $name . '"';
            }
            my @pre     = ( undef );
            my $choices = [ @pre, map( $_->{text}, @$sub_menu_insert ) ];
            # Choose
            my $idx = choose(
                $choices,
                { %{$self->{info}{lyt_3}}, index => 1, default => $old_idx, undef => $self->{info}{back_short}, prompt => $prompt }
            );
            if ( ! defined $idx || ! defined $choices->[$idx] ) {
                if ( $group =~ /^_module_/ ) {
                    $old_idx = $backup_old_idx;
                    $group = 'main_insert';
                    redo GROUP_INSERT;
                }
                else {
                    if ( $self->{info}{write_config} ) {
                        $self->__write_config_files();
                        delete $self->{info}{write_config};
                    }
                    return
                }
            }
            if ( $self->{opt}{G}{menus_config_memory} ) {
                if ( $old_idx == $idx ) {
                    $old_idx = 0;
                    next OPTION_INSERT;
                }
                $old_idx = $idx;
            }
            else {
                if ( $old_idx != 0 ) {
                    $old_idx = 0;
                    next OPTION_INSERT;
                }
            }
            my $option = $idx <= $#pre ? $pre[$idx] : $sub_menu_insert->[$idx - @pre]{name};
            if ( $option =~ /^_module_/ ) {
                $backup_old_idx = $old_idx;
                $old_idx = 0;
                $group = $option;
                redo GROUP_INSERT;
            }
            my $section  = $sub_menu_insert->[$idx - @pre]{section};
            my $opt_type = 'opt';
            my $no_yes   = [ 'NO', 'YES' ];
            if ( $option eq 'input_modes' ) {
                    my $available = [ 'Cols', 'Rows', 'Multi-row', 'File' ];
                    my $prompt = 'Input Modes:';
                    $self->__opt_choose_a_list( $opt_type, $section, $option, $available, $prompt );
            }
            elsif ( $option eq 'files_dir' ) {
                $self->__opt_choose_a_dir( $opt_type, $section, $option );
            }
            elsif ( $option eq 'file_encoding' ) {
                my $items = [
                    { name => 'file_encoding', prompt => "file_encoding" },
                ];
                my $prompt = 'Encoding CSV files';
                $self->__group_readline( $opt_type, $section, $items, $prompt );
            }

            elsif ( $option eq 'parse_mode' ) {
                my $prompt = 'Parsing CSV files';
                my $list = [ 'Text::CSV', 'split', 'Spreadsheet::Read' ];
                my $sub_menu = [ [ $option, "  Use", $list ] ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
            }
            elsif ( $option eq '_csv_char' ) {
                my $items = [
                    { name => 'sep_char',    prompt => "sep_char   " },
                    { name => 'quote_char',  prompt => "quote_char " },
                    { name => 'escape_char', prompt => "escape_char" },
                ];
                my $prompt = '"Text::CSV"';
                $self->__group_readline( $opt_type, $section, $items, $prompt );
            }
            elsif ( $option eq '_options_csv' ) {
                my $prompt = '"Text::CSV"';
                my $sub_menu = [
                    [ 'allow_loose_escapes', "- allow_loose_escapes", [ 'NO', 'YES' ] ],
                    [ 'allow_loose_quotes',  "- allow_loose_quotes",  [ 'NO', 'YES' ] ],
                    [ 'allow_whitespace',    "- allow_whitespace",    [ 'NO', 'YES' ] ],
                    [ 'blank_is_undef',      "- blank_is_undef",      [ 'NO', 'YES' ] ],
                    [ 'empty_is_undef',      "- empty_is_undef",      [ 'NO', 'YES' ] ],
                ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
            }
            elsif ( $option eq '_parse_with_split' ) {
                my $items = [
                    { name => 'i_r_s', prompt => "IRS" },
                    { name => 'i_f_s', prompt => "IFS" },
                ];
                my $prompt = 'Separators (regexp)';
                $self->__group_readline( $opt_type, $section, $items, $prompt );
            }

            elsif ( $option eq 'create_table_defaults' ) {
                my $items = [
                    { name => 'id_col_name',       prompt => "Default ID col name  " },
                    { name => 'default_data_type', prompt => "Default data type" },
                ];
                my $prompt = 'Create-table defaults';
                $self->__group_readline( $opt_type, $section, $items, $prompt );
            }
            else { die "Unknown option: $option" }
        }
    }
}


sub __menus {
    my ( $self, $group ) = @_;
    my $menus = {
        main => [
            { name => 'help',            text => "  HELP"   },
            { name => 'path',            text => "  Path"   },
            { name => 'config_database', text => "- DB"     },
            { name => 'config_menu',     text => "- Menu"   },
            { name => 'config_sql',      text => "- SQL",   },
            { name => 'config_output',   text => "- Output" },
            { name => 'config_insert',   text => "- Insert" },
        ],
        config_database => [
            { name => '_db_defaults', text => "- DB Settings"                },
            { name => 'db_plugins',   text => "- DB Plugins", section => 'G' },
        ],
        config_menu => [
            { name => '_menu_memory',  text => "- Menu Memory", section => 'G' },
            { name => '_table_expand', text => "- Table",       section => 'table' },
            { name => 'mouse',         text => "- Mouse Mode",  section => 'table' },
        ],
        config_sql => [
            { name => 'metadata',     text => "- Metadata",    section => 'G' },
            { name => 'operators',    text => "- Operators",   section => 'G' },
            { name => 'lock_stmt',    text => "- Lock Mode",   section => 'G' },
            { name => '_parentheses', text => "- Parentheses", section => 'G' },

        ],
        config_output => [
            { name => 'max_rows',      text => "- Max Rows",    section => 'table' },
            { name => 'min_col_width', text => "- Colwidth",    section => 'table' },
            { name => 'progress_bar',  text => "- ProgressBar", section => 'table' },
            { name => 'tab_width',     text => "- Tabwidth",    section => 'table' },
            { name => 'undef',         text => "- Undef",       section => 'table' },
        ],
    };
    return $menus->{$group};
}


sub __set_options {
    my ( $self ) = @_;
    my $group = 'main';
    my $backup_old_idx = 0;
    my $old_idx = 0;

    GROUP: while ( 1 ) {
        my $menu = $self->__menus( $group );

        OPTION: while ( 1 ) {
            my $back =          $group eq 'main' ? $self->{info}{_quit}     : $self->{info}{back_short};
            my @pre  = ( undef, $group eq 'main' ? $self->{info}{_continue} : () );
            my $choices = [ @pre, map( $_->{text}, @$menu ) ];
            # Choose
            my $idx = choose(
                $choices,
                { %{$self->{info}{lyt_3}}, index => 1, default => $old_idx, undef => $back }
            );
            if ( ! defined $idx || ! defined $choices->[$idx] ) {
                if ( $group =~ /^config_/ ) {
                    $old_idx = $backup_old_idx;
                    $group = 'main';
                    redo GROUP;
                }
                else {
                    if ( $self->{info}{write_config} ) {
                        $self->__write_config_files();
                        delete $self->{info}{write_config};
                    }
                    exit();
                }
            }
            if ( $self->{opt}{G}{menus_config_memory} ) {
                if ( $old_idx == $idx ) {
                    $old_idx = 0;
                    next OPTION;
                }
                $old_idx = $idx;
            }
            else {
                if ( $old_idx != 0 ) {
                    $old_idx = 0;
                    next OPTION;
                }
            }
            my $option = $idx <= $#pre ? $pre[$idx] : $menu->[$idx - @pre]{name};
            if ( $option eq 'config_insert' ) {
                $backup_old_idx = $old_idx;
                $self->__config_insert();
                $old_idx = $backup_old_idx;
                $group = 'main';
                redo GROUP;
            }
            elsif ( $option =~ /^config_/ ) {
                $backup_old_idx = $old_idx;
                $old_idx = 0;
                $group = $option;
                redo GROUP;
            }
            elsif ( $option eq $self->{info}{_continue} ) {
                if ( $self->{info}{write_config} ) {
                    $self->__write_config_files();
                    delete $self->{info}{write_config};
                }
                return $self->{opt}, $self->{db_opt};
            }
            elsif ( $option eq 'help' ) {
                require Pod::Usage;
                Pod::Usage::pod2usage( {
                    -exitval => 'NOEXIT',
                    -verbose => 2 } );
                next OPTION;
            }
            elsif ( $option eq 'path' ) {
                my $version = 'version';
                my $bin     = '  bin  ';
                my $app_dir = 'app-dir';
                my $path = {
                    $version => $main::VERSION,
                    $bin     => catfile( $RealBin, $RealScript ),
                    $app_dir => $self->{info}{app_dir},
                };
                my $names = [ $version, $bin, $app_dir ];
                print_hash( $path, { keys => $names, preface => ' Close with ENTER' } );
                next OPTION;
            }
            elsif ( $option eq '_db_defaults' ) {
                $self->__database_setting();
                next OPTION;
            }
            my $opt_type = 'opt';
            my $section  = $menu->[$idx - @pre]{section};
            my $no_yes   = [ 'NO', 'YES' ];
            if ( $option eq 'db_plugins' ) {
                my %installed_db_driver;
                for my $dir ( @INC ) {
                    my $glob_pattern = catfile $dir, 'App', 'DBBrowser', 'DB', '*.pm';
                    map { $installed_db_driver{( fileparse $_, '.pm' )[0]}++ } glob $glob_pattern;
                }
                my $prompt = 'Choose DB plugins:';
                $self->__opt_choose_a_list( $opt_type, $section, $option, [ sort keys %installed_db_driver ], $prompt );
                $self->__read_db_config_files();
            }
            elsif ( $option eq 'tab_width' ) {
                my $digits = 3;
                my $prompt = '"Tab width"';
                $self->__opt_number_range( $opt_type, $section, $option, $prompt, $digits );
            }
            elsif ( $option eq 'min_col_width' ) {
                my $digits = 3;
                my $prompt = '"Min column width"';
                $self->__opt_number_range( $opt_type, $section, $option, $prompt, $digits );
            }
            elsif ( $option eq 'undef' ) {
                my $items = [
                    { name => 'undef', prompt => "undef" },
                ];
                my $prompt = 'Print replacement for undefined table values.';
                $self->__group_readline( $opt_type, $section, $items, $prompt );

            }
            elsif ( $option eq 'progress_bar' ) {
                my $digits = 7;
                my $prompt = '"Threshold ProgressBar"';
                $self->__opt_number_range( $opt_type, $section, $option, $prompt, $digits );
            }
            elsif ( $option eq 'max_rows' ) {
                my $digits = 7;
                my $prompt = '"Max rows"';
                $self->__opt_number_range( $opt_type, $section, $option, $prompt, $digits );
            }
            elsif ( $option eq 'lock_stmt' ) {
                my $prompt = 'SQL statement: ';
                my $list = [ 'Lk0', 'Lk1' ];
                my $sub_menu = [ [ $option, "  Lock Mode", $list ] ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
            }
            elsif ( $option eq 'metadata' ) {
                my $prompt = 'DB/schemas/tables: ';
                my $list = $no_yes;
                my $sub_menu = [ [ $option, "  Add metadata", $list ] ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
            }
            elsif ( $option eq '_parentheses' ) {
                my $sub_menu = [
                    [ 'parentheses_w', "- Parens in WHERE",     [ 'NO', 'YES' ] ],
                    [ 'parentheses_h', "- Parens in HAVING TO", [ 'NO', 'YES' ] ],
                ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu );
            }
            elsif ( $option eq 'operators' ) {
                my $available = $self->{info}{avail_operators};
                my $prompt = 'Choose operators:';
                $self->__opt_choose_a_list( $opt_type, $section, $option, $available, $prompt );
            }
            elsif ( $option eq 'mouse' ) {
                my $prompt = 'Choose: ';
                my $list = [ 0, 1, 2, 3, 4 ];
                my $sub_menu = [ [ $option, "  Mouse mode", $list ] ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
            }
            elsif ( $option eq '_menu_memory' ) {
                my $prompt = 'Choose: ';
                my $sub_menu = [
                    [ 'menus_config_memory', "- Config Menus", [ 'Simple', 'Memory' ] ],
                    [ 'menu_sql_memory',     "- SQL    Menu",  [ 'Simple', 'Memory' ] ],
                    [ 'menus_db_memory',     "- DB     Menus", [ 'Simple', 'Memory' ] ],
                ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
            }
            elsif ( $option eq '_table_expand' ) {
                my $prompt = 'Choose: ';
                my $sub_menu = [
                    [ 'keep_header',  "- Table Header", [ 'Simple', 'Each page' ] ],
                    [ 'table_expand', "- Table Rows",   [ 'Simple', 'Expand fast back', 'Expand' ] ],
                ];
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
            }
            else { die "Unknown option: $option" }
        }
    }
}


sub __opt_choose_multi {
    my ( $self, $opt_type, $section, $sub_menu, $prompt ) = @_;
    my $changed = choose_multi( $sub_menu, $self->{$opt_type}{$section}, { prompt => $prompt } );
    return if ! $changed;
    $self->{info}{write_config}++;
}


sub __opt_choose_a_list {
    my ( $self, $opt_type, $section, $option, $available, $prompt ) = @_;
    my $current = $self->{$opt_type}{$section}{$option};
    # Choose_list
    my $list = choose_a_subset( $available, { prompt => $prompt, current => $current, index => 0 } );
    return if ! defined $list;
    return if ! @$list;
    $self->{$opt_type}{$section}{$option} = $list;
    $self->{info}{write_config}++;
    return;
}


sub __opt_number_range {
    my ( $self, $opt_type, $section, $option, $prompt, $digits ) = @_;
    my $current = $self->{$opt_type}{$section}{$option};
    $current = insert_sep( $current, $self->{opt}{G}{thsd_sep} );
    # Choose_a_number
    my $choice = choose_a_number( $digits, { name => $prompt, current => $current } );
    return if ! defined $choice;
    $self->{$opt_type}{$section}{$option} = $choice eq '--' ? undef : $choice;
    $self->{info}{write_config}++;
    return;
}


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


sub __opt_choose_a_dir {
    my ( $self, $opt_type, $section, $option ) = @_;
    my $current = $self->{$opt_type}{$section}{$option};
    # Choose_a_dir
    my $dir = choose_a_dir( { mouse => $self->{opt}{table}{mouse}, current => $current } );
    return if ! length $dir;
    $self->{$opt_type}{$section}{$option} = $dir;
    $self->{info}{write_config}++;
    return;
}


sub __opt_choose_dirs {
    my ( $self, $opt_type, $section, $option ) = @_;
    my $current = $self->{$opt_type}{$section}{$option};
    # Choose_dirs
    my $dirs = choose_dirs( { mouse => $self->{opt}{table}{mouse}, current => $current } );
    return if ! defined $dirs;
    return if ! @$dirs;
    $self->{$opt_type}{$section}{$option} = $dirs;
    $self->{info}{write_config}++;
    return;
}


sub __database_setting {
    my ( $self, $db ) = @_;
    my $changed = 0;
    SECTION: while ( 1 ) {
        my ( $db_driver, $db_plugin, $section );
        if ( defined $db ) {
            $db_plugin = $self->{info}{db_plugin};
            $db_driver = $self->{info}{db_driver};
            $section   = $db_plugin . '_' . $db;
            for my $option ( keys %{$self->{opt}{$db_plugin}} ) {
                next if $option eq 'directories_sqlite';
                if ( ! defined $self->{opt}{$section}{$option} ) {
                    $self->{opt}{$section}{$option} = $self->{opt}{$db_plugin}{$option};
                }
            }
        }
        else {
            if ( @{$self->{opt}{G}{db_plugins}} == 1 ) {
                $db_plugin = $self->{opt}{G}{db_plugins}[0];
            }
            else {
                # Choose
                $db_plugin = choose(
                    [ undef, map( "- $_", @{$self->{opt}{G}{db_plugins}} ) ],
                    { %{$self->{info}{lyt_3}}, undef => $self->{info}{back_short} }
                );
                return if ! defined $db_plugin;
            }
            $db_plugin =~ s/^-\ //;
            $self->{info}{db_plugin} = $db_plugin;
            $section = $db_plugin;
        }
        my $obj_db = App::DBBrowser::DB->new( $self->{info}, $self->{opt} );
        $db_driver = $obj_db->db_driver() if ! $db_driver;
        my $env_variables = $obj_db->environment_variables();
        my $login_data    = $obj_db->read_arguments();
        my $connect_attr  = $obj_db->choose_arguments();
        my $items = {
            required => [ map { {
                    name         => 'field_' . $_->{name},
                    prompt       => exists $_->{prompt} ? $_->{prompt} : $_->{name},
                    avail_values => [ 'NO', 'YES' ]
                } } @$login_data ],
            env_variables => [ map { {
                    name         => $_,
                    prompt       => $_,
                    avail_values => [ 'NO', 'YES' ]
                } } @$env_variables ],
            read_argument   => [
                    grep { ! $_->{keep_secret} } @$login_data
                ],
            choose_argument => $connect_attr,
        };
        push @{$items->{choose_argument}}, {
            name          => 'binary_filter',
            avail_values  => [ 0, 1 ],
            default_index => 0,
        };
        my @groups;
        push @groups, [ 'required',        "- Fields"             ] if @{$items->{required}};
        push @groups, [ 'env_variables',   "- ENV Variables"      ] if @{$items->{env_variables}};
        push @groups, [ 'read_argument',   "- Login Data"         ] if @{$items->{read_argument}};
        push @groups, [ 'choose_argument', "- DB Options"         ];
        push @groups, [ 'sqlite_dir',      "- Sqlite directories" ] if $db_driver eq 'SQLite';
        my $prompt = defined $db ? 'DB: "' . ( $db_driver eq 'SQLite' ? basename $db : $db )
                                 : 'Plugin "' . $db_plugin . '"';
        my $opt_type = 'db_opt';
        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
            my $idx_group = choose(
                $choices,
                { %{$self->{info}{lyt_3}}, prompt => $prompt, index => 1,
                  default => $old_idx_group, undef => $self->{info}{back_short} }
            );
            if ( ! defined $idx_group || ! defined $choices->[$idx_group] ) {
                if ( $self->{info}{write_config} ) {
                    $self->__write_db_config_files();
                    delete $self->{info}{write_config};
                    $changed++;
                }
                next SECTION if ! $db && @{$self->{opt}{G}{db_plugins}} > 1;
                return $changed;
            }
            if ( $self->{opt}{G}{menus_config_memory} ) {
                if ( $old_idx_group == $idx_group ) {
                    $old_idx_group = 0;
                    next GROUP;
                }
                else {
                    $old_idx_group = $idx_group;
                }
            }
            if ( $choices->[$idx_group] eq $reset ) {
                my @databases;
                for my $section ( keys %{$self->{db_opt}} ) {
                    push @databases, $1 if $section =~ /^\Q$db_plugin\E_(.+)\z/;
                }
                if ( ! @databases ) {
                    choose(
                        [ 'No databases with customized settings.' ],
                        { %{$self->{info}{lyt_stop}}, prompt => 'Press ENTER' }
                    );
                    next GROUP;
                }
                my $choices = choose_a_subset(
                    [ sort @databases ],
                    { p_new => 'Reset DB: ' }
                );
                if ( ! $choices->[0] ) {
                    next GROUP;
                }
                for my $db ( @$choices ) {
                    my $section = $db_plugin . '_' . $db;
                    delete $self->{db_opt}{$section};
                }
                $self->{info}{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->{avail_values} ];
                    if ( ! defined $self->{db_opt}{$section}{$required} ) {
                        if ( defined $self->{db_opt}{$db_plugin}{$required} ) {
                            $self->{db_opt}{$section}{$required} = $self->{db_opt}{$db_plugin}{$required};
                        }
                        else {
                            $self->{db_opt}{$section}{$required} = 1;  # All fields required by default
                        }
                    }
                }
                my $prompt = 'Required fields (' . $db_plugin . '):';
                $self->__opt_choose_multi( $opt_type, $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->{avail_values} ];
                    if ( ! defined $self->{db_opt}{$section}{$env_variable} ) {
                        if ( defined $self->{db_opt}{$db_plugin}{$env_variable} ) {
                            $self->{db_opt}{$section}{$env_variable} = $self->{db_opt}{$db_plugin}{$env_variable};
                        }
                        else {
                            $self->{db_opt}{$section}{$env_variable} = 0;
                        }
                    }
                }
                my $prompt = 'Use ENV variables (' . $db_plugin . '):';
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
                next GROUP;
            }
            elsif ( $group eq 'read_argument' ) {
               for my $item ( @{$items->{$group}} ) {
                    my $option = $item->{name};
                    if ( ! defined $self->{db_opt}{$section}{$option} ) {
                        if ( defined $self->{db_opt}{$db_plugin}{$option} ) {
                            $self->{db_opt}{$section}{$option} = $self->{db_opt}{$db_plugin}{$option};
                        }
                    }
                }
                my $prompt = 'Default login data (' . $db_plugin . '):';
                $self->__group_readline( $opt_type, $section, $items->{$group}, $prompt );
            }
            elsif ( $group eq 'choose_argument' ) {
                my $sub_menu = [];
                for my $item ( @{$items->{$group}} ) {
                    my $option = $item->{name};
                    my $prompt = '- ' . ( exists $item->{prompt} ? $item->{prompt} : $item->{name} );
                    push @$sub_menu, [ $option, $prompt, $item->{avail_values} ];
                    if ( ! defined $self->{db_opt}{$section}{$option} ) {
                        if ( defined $self->{db_opt}{$db_plugin}{$option} ) {
                            $self->{db_opt}{$section}{$option} = $self->{db_opt}{$db_plugin}{$option};
                        }
                        else {
                            $self->{db_opt}{$section}{$option} = $item->{avail_values}[$item->{default_index}];
                        }
                    }
                }
                my $prompt = 'Options (' . $db_plugin . '):';
                $self->__opt_choose_multi( $opt_type, $section, $sub_menu, $prompt );
                next GROUP;
            }
            elsif ( $group eq 'sqlite_dir' ) {
                my $option = 'directories_sqlite';
                $self->__opt_choose_dirs( $opt_type, $section, $option );
                next GROUP;
            }
        }
    }
}


sub __write_config_files {
    my ( $self ) = @_;
    my $fmt = $self->{info}{conf_file_fmt};
    my $tmp = {};
    for my $section ( keys %{$self->{opt}} ) {
        for my $option ( keys %{$self->{opt}{$section}} ) {
            $tmp->{$section}{$option} = $self->{opt}{$section}{$option};
        }
    }
    my $auxil = App::DBBrowser::Auxil->new( $self->{info} );
    my $file_name = $self->{info}{config_generic};
    $auxil->__write_json( sprintf( $fmt, $file_name ), $tmp  );
}


sub __write_db_config_files {
    my ( $self ) = @_;
    my $regexp_db_plugins = join '|', map quotemeta, @{$self->{opt}{G}{db_plugins}};
    my $fmt = $self->{info}{conf_file_fmt};
    my $tmp = {};
    for my $section ( sort keys %{$self->{db_opt}} ) {
        if ( $section =~ /^($regexp_db_plugins)(?:_(.+))?\z/ ) {
            my ( $db_plugin, $conf_sect ) = ( $1, $2 );
            $conf_sect = '*' . $db_plugin if ! defined $conf_sect;
            for my $option ( keys %{$self->{db_opt}{$section}} ) {
                $tmp->{$db_plugin}{$conf_sect}{$option} = $self->{db_opt}{$section}{$option};
            }
        }
    }
    my $auxil = App::DBBrowser::Auxil->new( $self->{info} );
    for my $section ( keys %$tmp ) {
        my $file_name =  $section;
        $auxil->__write_json( sprintf( $fmt, $file_name ), $tmp->{$section}  );
    }
}


sub __read_config_files {
    my ( $self ) = @_;
    $self->{opt} = $self->defaults();
    my $fmt = $self->{info}{conf_file_fmt};
    my $auxil = App::DBBrowser::Auxil->new( $self->{info} );
    my $file =  sprintf( $fmt, $self->{info}{config_generic} );
    if ( -f $file && -s $file ) {
        my $tmp = $auxil->__read_json( $file );
        for my $section ( keys %$tmp ) {
            for my $option ( keys %{$tmp->{$section}} ) {
                $self->{opt}{$section}{$option} = $tmp->{$section}{$option} if exists $self->{opt}{$section}{$option};
            }
        }
    }
    return $self->{opt};
}


sub __read_db_config_files {
    my ( $self ) = @_;
    my $fmt = $self->{info}{conf_file_fmt};
    my $auxil = App::DBBrowser::Auxil->new( $self->{info} );
    for my $db_plugin ( @{$self->{opt}{G}{db_plugins}} ) {
        my $file = sprintf( $fmt, $db_plugin );
        if ( -f $file && -s $file ) {
            my $tmp = $auxil->__read_json( $file );
            for my $conf_sect ( keys %$tmp ) {
                my $section = $db_plugin . ( $conf_sect =~ /^\*\Q$db_plugin\E\z/ ? '' : '_' . $conf_sect );
                for my $option ( keys %{$tmp->{$conf_sect}} ) {
                    $self->{db_opt}{$section}{$option} = $tmp->{$conf_sect}{$option};
                }
            }
        }
    }
    return $self->{db_opt};
}




1;


__END__