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

use warnings;
use strict;
use 5.010000;

use Exporter qw( import );
our @EXPORT_OK = qw( read_history_files uploader_history_menu add_uploader_to_history new_uploader_to_history_file );

use List::MoreUtils  qw( any none );
use LWP::UserAgent   qw();
use Term::ANSIScreen qw( :cursor :screen );
use Term::Choose     qw( choose );

use if $^O eq 'MSWin32', 'Win32::Console::ANSI';

use App::YTDL::Helper qw( read_json write_json );



sub read_history_files {
    my ( $opt ) = @_;
    $opt->{sticky}  = {};
    $opt->{history} = {};
    if ( $opt->{max_size_history} ) {
        $opt->{sticky}  = read_json( $opt, $opt->{sticky_file} )  // {} if -e $opt->{sticky_file};
        $opt->{history} = read_json( $opt, $opt->{history_file} ) // {} if -e $opt->{history_file};
    }
}


sub uploader_history_menu {
    my ( $opt ) = @_;

    MENU : while ( 1 ) {
        my @list_size = ( '  ALL', '  50' );
        my $list_size_regexp = join '|', map { quotemeta } @list_size;
        my ( $uploader, $sticky ) = ( '- Uploader', '  Sticky' );
        # Choose
        my $choice = choose(
            [ undef, $uploader, $sticky, $list_size[$opt->{small_list_size}] ],
            { prompt => 'Choose:', layout => 3, undef => '  QUIT' }
        );
        if ( ! defined $choice ) {
            exit;
        }
        elsif ( $choice eq $uploader ) {
            my ( $sticky,  $sticky_url ) = _sticky_name_and_url( $opt->{sticky} );
            my ( $history, $history_url, $removed_next ) = _history_name_and_url( $opt, $opt->{history} );
            my $prompt = 'Chosen:' . "\n";
            my @ids;
            my $mark = '*';

            CHANNEL: while ( 1 ) {
                my $confirm = '  CONFIRM';
                my $hidden  = 'Your choice:' . $mark;
                my @pre = ( $hidden, undef, $confirm );
                my $choices = _available_uploader( \@pre, $sticky, $history, $removed_next );
                # Choose
                my @indexes = choose(
                    $choices,
                    { prompt => $prompt, layout => 3, index => 1, default => 1,
                        undef => '  BACK', no_spacebar => [ 0 .. $#pre ] }
                );
                if ( ! defined $indexes[0] || ! defined $choices->[$indexes[0]] ) {
                    if ( @ids ) {
                        $prompt = 'Channels:' . "\n";
                        ( $sticky,  $sticky_url ) = _sticky_name_and_url( $opt->{sticky} );
                        ( $history, $history_url, $removed_next ) = _history_name_and_url( $opt, $opt->{history} );
                        @ids = ();
                        $mark = '*';
                        next CHANNEL;
                    }
                    else {
                        next MENU;
                    }
                }
                else {
                    if ( $choices->[$indexes[0]] eq $hidden ) {
                        $prompt = 'Chosen:' . "\n";
                        ( $sticky,  $sticky_url ) = _sticky_name_and_url( $opt->{sticky} );
                        ( $history, $history_url, $removed_next ) = _history_name_and_url( $opt, $opt->{history} );
                        @ids = ();
                        for my $i ( 0 .. $#$sticky_url ) {
                            $prompt .= sprintf "= %s (%s)\n", $sticky->[$i], $opt->{sticky}{$sticky_url->[$i]}{uploader_id};
                            push @ids, $sticky_url->[$i];
                        }
                        @$sticky_url = ();
                        @$sticky     = ();
                        $mark = '';
                        next CHANNEL;
                    }
                    my $confirmed;
                    if ( $choices->[$indexes[0]] eq $confirm ) {
                        shift @indexes;
                        $confirmed = 1;
                    }
                    my $count = 0;
                    for my $i ( @indexes ) {
                        $i -= @pre + $count;
                        if ( $i <= $#$sticky ) {
                            $prompt .= sprintf "= %s (%s)\n", $sticky->[$i], $opt->{sticky}{$sticky_url->[$i]}{uploader_id};
                            push @ids, splice @$sticky_url, $i, 1;
                                       splice @$sticky    , $i, 1;
                        }
                        else {
                            $i -= @$sticky;
                            $prompt .= sprintf "= %s (%s)\n", $history->[$i], $opt->{history}{$history_url->[$i]}{uploader_id};
                            push @ids, splice @$history_url,  $i, 1;
                                       splice @$history,      $i, 1;
                                       splice @$removed_next, $i, 1;
                        }
                        $count++;
                    }
                    $mark = '*';
                    return @ids if $confirmed;
                }
            }
        }
        elsif ( $choice eq $sticky ) {
            my ( $sticky,  $sticky_url )  = _sticky_name_and_url( $opt->{sticky} );
            my ( $history, $history_url ) = _history_name_and_url( $opt, $opt->{history} );
            my $changed = 0;

            STICKY: while ( 1 ) {
                my @pre = ( undef );
                my $idx = choose(
                    [ @pre, map( "+ $_", @$sticky ), map( "- $_", @$history ) ],
                    { prompt => 'Stickify:', layout => 3, index => 1, undef => '  <<' }
                );
                if ( ! $idx ) {
                    if ( $changed ) {
                        write_json( $opt, $opt->{sticky_file},  $opt->{sticky} );
                        write_json( $opt, $opt->{history_file}, $opt->{history} );
                    }
                    next MENU;
                }
                else {
                    $changed++;
                    $idx-= @pre;
                    if ( $idx > $#$sticky ) {
                        $idx -= @$sticky;
                        my $uploader_url = $history_url->[$idx];
                        $opt->{sticky}{$uploader_url} = delete $opt->{history}{$uploader_url};
                    }
                    else {
                        my $uploader_url = $sticky_url->[$idx];
                        $opt->{history}{$uploader_url} = delete $opt->{sticky}{$uploader_url};
                        $opt->{history}{$uploader_url}{timestamp} = time();
                    }
                    ( $sticky,  $sticky_url )  = _sticky_name_and_url( $opt->{sticky} );
                    ( $history, $history_url ) = _history_name_and_url( $opt, $opt->{history} );
                }
            }
        }
        if ( $choice =~ /^$list_size_regexp\z/ ) {
            $opt->{small_list_size}++;
            $opt->{small_list_size} = 0 if $opt->{small_list_size} > $#list_size;
            next MENU;
        }
    }
}


sub _available_uploader {
    my ( $pre, $sticky, $history, $removed_next ) = @_;
    my $choices = [ @$pre, map( "* $_", @$sticky ) ];
    for my $i ( 0 .. $#$history ) {
        push @$choices, "  $history->[$i]" if ! $removed_next->[$i];
        push @$choices, "| $history->[$i]" if   $removed_next->[$i];
    }
    return $choices;
}


sub _history_name_and_url {
    my ( $opt, $ref ) = @_;
    my $tmp_name = [];
    my $tmp_url  = [];
    my $tmp_removed_next = [];
    my $count = 0;
    for my $key ( sort { $ref->{$b}{timestamp} <=> $ref->{$a}{timestamp} } keys %$ref ) {
        $count++;
        push @$tmp_name, $ref->{$key}{uploader};
        push @$tmp_url, $key;
        push @$tmp_removed_next, $count >= $opt->{max_size_history} ? 1 : 0;
    }
    my $history_name = [];
    my $history_url  = [];
    my $removed_next = [];
    if ( ! $opt->{sort_history_by_timestamp} ) {
        for my $i ( sort { lc $tmp_name->[$a] cmp lc $tmp_name->[$b] } 0 .. $#$tmp_name ) {
            push @$history_name, $tmp_name->[$i];
            push @$history_url,  $tmp_url->[$i];
            push @$removed_next, $tmp_removed_next->[$i];
        }
    }
    else {
        $history_name = $tmp_name;
        $history_url  = $tmp_url;
        $removed_next = $tmp_removed_next;
    }
    return $history_name, $history_url, $removed_next;
}


sub _sticky_name_and_url {
    my ( $ref ) = @_;
    my $sticky_name = [];
    my $sticky_url  = [];
    for my $key ( sort { lc $ref->{$a}{uploader} cmp lc $ref->{$b}{uploader} } keys %$ref ) {
        push @$sticky_name, $ref->{$key}{uploader};
        push @$sticky_url, $key;
    }
    return $sticky_name, $sticky_url;
}


sub add_uploader_to_history {
    my ( $opt, $info, $ex, $video_id ) = @_;
    my $uploader    = $info->{$ex}{$video_id}{uploader};
    my $uploader_id = $info->{$ex}{$video_id}{uploader_id};
    return if ! length $uploader_id;
    my $uploader_url;
    if ( $ex eq 'youtube' ) {
        my $user_url    = sprintf 'https://www.youtube.com/user/%s', $uploader_id;
        my $channel_url = sprintf 'https://www.youtube.com/channel/%s', $uploader_id;
        return if any { $user_url eq $_ || $channel_url eq $_ } keys %{$opt->{sticky}};
        return if any { $user_url eq $_ || $channel_url eq $_ } keys %{$opt->{history}};
        my @type = ( length $uploader_id == 24 ? ( 'channel', 'user' ) : ( 'user', 'channel' ) );
        my $ua = LWP::UserAgent->new(
            agent         => $opt->{useragent},
            timeout       => $opt->{timeout},
            show_progress => 1
        );
        my $url = sprintf 'https://www.youtube.com/%s/%s', $type[0], $uploader_id;
        my $res = $ua->head( $url );
        print up( 1 ), cldown;
        if ( $res->is_success ) {
            $uploader_url = sprintf 'https://www.youtube.com/%s/', $type[0];
        }
        else {
            my $url = sprintf 'https://www.youtube.com/%s/%s', $type[1], $uploader_id;
            my $res = $ua->head( $url );
            print up( 1 ), cldown;
            if ( $res->is_success ) {
                $uploader_url = sprintf 'https://www.youtube.com/%s/', $type[1];
            }
        }
    }
    elsif ( $ex eq 'vimeo' ) {
        $uploader_url = 'https://vimeo.com/';
    }
    if ( defined $uploader_url ) {
        $uploader_url .= $uploader_id;
        if ( none{ $uploader_url eq $_ } keys %{$opt->{sticky}} ) {
            $opt->{history}{$uploader_url} = {
                uploader     => $uploader,
                uploader_id  => $uploader_id,
                extractor    => $ex,
                timestamp    => time(),
            };
        }
    }
}


sub new_uploader_to_history_file {
    my ( $opt ) = @_;
    my $c_hist = $opt->{history};
    my @keys = sort { $c_hist->{$b}{timestamp} <=> $c_hist->{$a}{timestamp} } keys %$c_hist;
    while ( @keys > $opt->{max_size_history} ) {
        my $key = pop @keys;
        delete $c_hist->{$key};
        @keys = sort { $c_hist->{$b}{timestamp} <=> $c_hist->{$a}{timestamp} } keys %$c_hist;
    }
    write_json( $opt, $opt->{history_file}, $c_hist );
}


1;


__END__