package # hide from PAUSE
App::YTDL::Config;

use warnings;
use strict;
use 5.010000;

use Exporter qw( import );
our @EXPORT_OK = qw( read_config_file set_options sort_order_list );

use File::Spec::Functions qw( catfile );
use File::Temp            qw();
use FindBin               qw( $RealBin $RealScript );
use List::Util            qw( max );
use Pod::Usage            qw( pod2usage );

use File::Which            qw( which );
use IPC::System::Simple    qw( capture );
use List::MoreUtils        qw( first_index );
use Term::Choose           qw( choose );
use Term::ReadLine::Simple qw();
use Text::LineFold         qw();
use Try::Tiny              qw( try catch );

use App::YTDL::Helper qw( term_size print_hash encode_fs choose_a_dir choose_a_number
                          insert_sep write_json read_json HIDE_CURSOR SHOW_CURSOR );



sub _menus {
    my $menus = {
        main => [
            [ 'show_help_text',  "  HELP"       ],
            [ 'show_info',       "  INFO"       ],
            [ 'group_directory', "- Directory"  ],
            [ 'group_file',      "- File"       ],
            [ 'group_quality',   "- Quality"    ],
            [ 'group_download',  "- Download"   ],
            [ 'group_history',   "- History"    ],
            [ 'group_list_menu', "- List menu"  ],
            [ 'group_output',    "- Appearance" ],
            [ 'list_extractors', "  Extractors" ],
        ],
        group_download => [
            [ 'useragent',      "- UserAgent"           ],
            [ 'overwrite',      "- Overwrite"           ],
            [ 'retries',        "- Download retries"    ],
            [ 'timeout',        "- Timeout"             ],
        ],
        group_quality => [
            [ 'auto_quality',    "- Auto quality mode"   ],
            [ 'print_pref_qual', "- Preferred qualities" ],
        ],
        group_history => [
            [ 'log_info',             "- Logging"         ],
            [ 'max_channels',         "- Channel history" ],
            [ 'channel_by_timestamp', "- History sort"    ],
        ],
        group_directory => [
            [ 'video_dir',     "- Main video directory" ],
            [ 'extractor_dir', "- Extractor directory"  ],
            [ 'channel_dir',   "- Channel directory"    ],
        ],
        group_file => [
            [ 'max_len_f_name',           "- Max filename length" ],
            [ 'upload_date_is_file_date', "- File timestamp"      ],
        ],
        group_list_menu => [
            [ 'list_size_idx',   "- List size" ],
            [ 'sort_item',       "- Sort order"      ],
            [ 'show_view_count', "- View count" ],
        ],
        group_output => [
            [ 'max_info_width',  "- Max info width"  ],
            [ 'kb_sec_len',      "- Digits 'k/s'"    ],
        ],
    };
    return $menus;
}


sub set_options {
    my ( $opt ) = @_;
    my $menus = _menus();
    my @keys;
    for my $group ( keys %$menus ) {
        next if $group eq 'main';
        push @keys, map { $_->[0] } @{$menus->{$group}};
    }
    my $group = 'main';
    my $backup_old_idx = 0;
    my $old_idx = 0;

    GROUP: while ( 1 ) {
        my $menu = $menus->{$group};

        OPTION: while ( 1 ) {
            my $back     = $group eq 'main' ? '  QUIT' : '  <<';
            my $continue = '  CONTINUE';
            my @pre  = ( undef, $group eq 'main' ? $continue : () );
            my @real = map( $_->[1], @$menu );
            # Choose
            my $idx = choose(
                [ @pre, @real ],
                { prompt => "Options:", layout => 3, index => 1, clear_screen => 1, undef => $back, default => $old_idx }
            );
            if ( ! $idx ) {
                if ( $group eq 'main' ) {
                    _write_config_file( $opt, $opt->{config_file}, @keys );
                    exit;
                }
                else {
                    $old_idx = $backup_old_idx;
                    $group = 'main';
                    next GROUP;
                }
            }
            if ( $old_idx == $idx ) {
                $old_idx = 0;
                next OPTION;
            }
            $old_idx = $idx;
            my $choice = $idx <= $#pre ? $pre[$idx] : $menu->[$idx - @pre][0];
            if ( $choice =~ /^group_/ ) {
                $backup_old_idx = $old_idx;
                $old_idx = 0;
                $group = $choice;
                redo GROUP;
            }
            if ( $choice eq $continue ) {
                if ( $group =~ /^group_/ ) {
                    $group = 'main';
                    redo GROUP;
                }
                _write_config_file( $opt, $opt->{config_file}, @keys );
                last GROUP;
            }
            if ( $choice eq "show_help_text" ) {
                pod2usage( { -exitval => 'NOEXIT', -verbose => 2 } );
            }
            elsif ( $choice eq "show_info" ) {
                my $youtube_dl_version;
                try {
                    my $youtube_dl = which( 'youtube-dl' ) // 'youtube-dl';
                    $youtube_dl_version = capture( $youtube_dl, '--version' );
                };
                my $version     = '  version ';
                my $bin         = '    bin   ';
                my $video_dir   = ' video dir';
                my $config_dir  = 'config dir';
                my $youtube_dl  = 'youtube-dl';
                my $path = {
                    $version    => $main::VERSION,
                    $bin        => catfile( $RealBin, $RealScript ),
                    $video_dir  => $opt->{video_dir},
                    $config_dir => $opt->{config_dir},
                    $youtube_dl => $youtube_dl_version,

                };
                my $keys = [ $bin, $version, $video_dir, $config_dir ];
                push @$keys, $youtube_dl if $youtube_dl_version;
                print_hash( $path, { keys => $keys, preface => ' Close with ENTER' } );
            }
            elsif ( $choice eq "list_extractors" ) {
                print HIDE_CURSOR;
                say 'Close with ENTER';
                my @capture;
                try {
                    my $youtube_dl = which( 'youtube-dl' ) // 'youtube-dl';
                    @capture = capture( $youtube_dl, '--list-extractors' );

                }
                catch {
                    say "List extractors: $_";
                };
                print SHOW_CURSOR;
                if ( @capture ) {
                    choose(
                        [ map { "  $_" } @capture ],
                        { layout => 3 }
                    );
                }
            }
            elsif ( $choice eq "print_pref_qual" ) {
                my $pref = read_json( $opt->{preferred_file} );
                if ( ! %$pref ) {
                    choose( [ 'Close with ENTER' ], { prompt => 'No preferred qualities set.' } );
                }
                else {
                    my @p;
                    push @p, ' ';
                    for my $key ( sort keys %$pref ) {
                        push @p, "$key:";
                        my $c = 1;
                        for my $line ( @{$pref->{$key}} ) {
                            push @p, sprintf "%4d. %s\n", $c, '[' . join( ',', @$line ) . ']';
                            $c++;
                        }
                        push @p, ' ';
                    }
                    choose( \@p, { prompt => 'Close with ENTER', layout => 3, justify => 0 } );
                }
            }
            elsif ( $choice eq "useragent" ) {
                my $prompt = 'Set the UserAgent: ';
                _local_read_line( $opt, $choice, $prompt );
                $opt->{useragent} = 'Mozilla/5.0' if $opt->{useragent} eq '';
                $opt->{useragent} = ''            if $opt->{useragent} eq '""';
            }
            elsif ( $choice eq "overwrite" ) {
                my $prompt = 'Overwrite files';
                _opt_yes_no( $opt, $choice, $prompt );
            }
            elsif ( $choice eq "log_info" ) {
                my $prompt = 'Enable info-logging';
                _opt_yes_no( $opt, $choice, $prompt );
            }
            elsif ( $choice eq "auto_quality" ) {
                my $prompt = 'Auto quality';
                my $list = [
                    'choose always manually',
                    'keep choice for the respective Playlist/Channel if possible',
                    'keep choice for the extractor if possible',
                    'use preferred qualities',
                    'use always default (best) quality',
                ];
                _opt_choose_from_list( $opt, $choice, $prompt, $list );
            }
            elsif ( $choice eq "retries" ) {
                my $prompt = 'Download retries';
                my $digits = 3;
                _opt_number_range( $opt, $choice, $prompt, 3 )
            }
            elsif ( $choice eq "timeout" ) {
                my $prompt = 'Connection timeout (s)';
                my $digits = 3;
                _opt_number_range( $opt, $choice, $prompt, 3 )
            }
            elsif ( $choice eq "max_info_width" ) {
                my $prompt = 'Maximum Info width';
                my $digits = 3;
                _opt_number_range( $opt, $choice, $prompt, 3 )
            }
            elsif ( $choice eq "max_len_f_name" ) {
                my $prompt = 'Maximum filename length';
                my $digits = 3;
                _opt_number_range( $opt, $choice, $prompt, 3 )
            }
            elsif ( $choice eq "kb_sec_len" ) {
                my ( $min, $max ) = ( 3, 9 );
                my $prompt = 'Digits for "k/s" (download speed)';
                _opt_number( $opt, $choice, $prompt, $min, $max );
            }
            elsif ( $choice eq "video_dir" ) {
                my $prompt = 'Video directory';
                _opt_choose_a_directory( $opt, $choice, $prompt );
            }
            elsif ( $choice eq "max_channels" ) {
                my $prompt = 'Channelhistory: save x channels. Disabled if x is 0';
                my $digits = 3;
                _opt_number_range( $opt, $choice, $prompt, 3 )
            }
            elsif ( $choice eq "channel_by_timestamp" ) {
                my $prompt = 'Channel history:';
                my $list = [
                    'Sort by name',
                    'Sort by timestamp'
                ];
                _opt_choose_from_list( $opt, $choice, $prompt, $list );
            }
            elsif ( $choice eq "list_size_idx" ) {
                my $prompt = 'Channel/Playlist';
                my $list = [
                    'Show all videos',
                    'Show only the latest 50 videos',
                ];
                _opt_choose_from_list( $opt, $choice, $prompt, $list );
            }
            elsif ( $choice eq "extractor_dir" ) {
                my $prompt = 'Use extractor directories';
                my $list = [
                    'No',
                    'Yes',
                ];
                _opt_choose_from_list( $opt, $choice, $prompt, $list );
            }
            elsif ( $choice eq "channel_dir" ) {
                my $prompt = 'Use channel directories';
                my $list = [
                    'No',
                    'If chosen from a channel or list',
                    'Always',
                ];
                _opt_choose_from_list( $opt, $choice, $prompt, $list );
            }
            elsif ( $choice eq "upload_date_is_file_date" ) {
                my $prompt = 'Change the file timestamps to the upload date';
                _opt_yes_no( $opt, $choice, $prompt );
            }
            elsif ( $choice eq "sort_item" ) {
                sort_order_list( $opt );
            }
            elsif ( $choice eq "show_view_count" ) {
                my $prompt = 'Show view count in channel/playlist menu';
                my $list = [
                    'If ordered by view count',
                    'Always',
                ];
                _opt_choose_from_list( $opt, $choice, $prompt, $list );
            }
            else { die $choice }
        }
    }
    return;
}


sub _opt_choose_a_directory {
    my( $opt, $choice, $prompt ) = @_;
    my $new_dir = choose_a_dir( { dir => $opt->{$choice} } );
    return if ! defined $new_dir;
    if ( $new_dir ne $opt->{$choice} ) {
        if ( ! eval {
            my $fh = File::Temp->new( TEMPLATE => 'XXXXXXXXXXXXXXX', UNLINK => 1, DIR => $new_dir );
            1 }
        ) {
            print "$@";
            choose( [ 'Press Enter:' ], { prompt => '' } );
        }
        else {
            $opt->{$choice} = $new_dir;
            $opt->{change}++;
        }
    }
}


sub _local_read_line {
    my ( $opt, $section, $prompt ) = @_;
    my $current = $opt->{$section} // '';
    my $trs = Term::ReadLine::Simple->new();
    # Readline
    my $string = $trs->readline( $prompt, { default => $current } );
    $opt->{$section} = $string;
    $opt->{change}++;
    return;
}


sub _opt_yes_no {
    my ( $opt, $section, $prompt ) = @_;
    my ( $yes, $no ) = ( 'YES', 'NO' );
    my $current = $opt->{$section} ? $yes : $no;
    # Choose
    my $choice = choose(
        [ undef, $yes, $no ],
        { prompt => $prompt . ' [' . $current . ']:', layout => 1, undef => '<<' }
    );
    return if ! defined $choice;
    $opt->{$section} = $choice eq $yes ? 1 : 0;
    $opt->{change}++;
    return;
}


sub _opt_number_range {
    my ( $opt, $section, $prompt, $digits ) = @_;
    my $current = $opt->{$section};
    $current = insert_sep( $current );
    # Choose_a_number
    my $choice = choose_a_number( $digits, { name => $prompt, current => $current } );
    return if ! defined $choice;
    $opt->{$section} = $choice eq '--' ? undef : $choice;
    $opt->{change}++;
    return;
}


sub _opt_number {
    my ( $opt, $section, $prompt, $min, $max ) = @_;
    my $current = $opt->{$section};
    # Choose
    my $choice = choose(
        [ undef, $min .. $max ],
        { prompt => $prompt . ' [' . $current . ']:', layout => 1, justify => 1, order => 0, undef => '<<' }
    );
    return if ! defined $choice;
    $opt->{$section} = $choice;
    $opt->{change}++;
    return;
}


sub _opt_choose_from_list {
    my ( $opt, $section, $prompt, $list ) = @_;
    my @options;
    my $len = length( scalar @$list );
    for my $i ( 0 .. $#$list ) {
        push @options, sprintf "%*d => %s", $len, $i, $list->[$i];
    }
    $prompt .= ' [' . ( $opt->{$section} // '--' ) . ']';
    my $value = choose( [ undef, @options ], { prompt => $prompt, layout => 3, undef => '<<' } );
    return if ! defined $value;
    $value = ( split / => /, $value )[0];
    $opt->{$section} = $value;
    $opt->{change}++;
    return;
}


sub sort_order_list {
    my ( $opt ) = @_;
    my $sort_items = [
        { key => 'upload_date',    prompt => '"Upload date"' },
        { key => 'title',          prompt => '"Title"'       },
        { key => 'view_count_raw', prompt => '"View count"'  },
        { key => 'duration',       prompt => '"Duration"'    },
    ];
    my $first_index = first_index { $_->{key} eq $opt->{sort_item} } @$sort_items;
    my $prompt = sprintf 'Sort order ("%s"):', $sort_items->[$first_index]{prompt};
    my @pre = ( undef );
    my $choices = [ @pre, map( $_->{prompt}, @$sort_items ) ];
    my $idx = choose (
        $choices,
        { prompt => $prompt, layout => 1, index => 1, undef => '<<' }
    );
    if ( ! defined $idx || ! defined $choices->[$idx] ) {
        return;
    }
    $opt->{change}++;
    $idx -= @pre;
    $opt->{sort_item} = $sort_items->[$idx]{key};
    my $order_prompt = sprintf '%s:', $sort_items->[$idx]{prompt};
    my $order = choose (
        [ @pre, 'Desc', 'Asc' ],
        { prompt => $order_prompt, layout => 1, undef => '<<' }
    );
    if ( ! defined $order ) {
        return;
    }
    $opt->{sort_order} = $order;
    return;
}


sub _write_config_file {
    my ( $opt, $file, @keys ) = @_;
    return if ! $opt->{change};
    my $tmp = {};
    for my $section ( sort @keys ) {
        $tmp->{$section} = $opt->{$section};
    }
    write_json( $file, $tmp );
    delete $opt->{change};
}


sub read_config_file {
    my ( $opt, $file ) = @_;
    my $tmp = read_json( $file );
    my @keys = keys %$tmp;
    for my $section ( @keys ) {
        $opt->{$section} = $tmp->{$section};
    }
    return @keys;
}


1;


__END__
