#!/usr/bin/env perl
use warnings;
use strict;
use 5.010000;
use utf8;

our $VERSION = '0.318';

use Encode                qw( encode decode );
use File::Path            qw( make_path );
use File::Spec::Functions qw( catdir catfile curdir );
use Getopt::Long          qw( GetOptions );
use Pod::Usage            qw( pod2usage );

use Encode::Locale   qw( decode_argv );
use File::HomeDir    qw();
use File::Which      qw( which );
use Term::ANSIScreen qw( :cursor :screen );
use Term::Choose     qw( choose );
use Term::Form       qw();

use App::YTDL::Arguments  qw( from_arguments_to_choices );
use App::YTDL::Download   qw( download_youtube );
use App::YTDL::Helper     qw( uni_capture );
use App::YTDL::History    qw( add_uploaders_to_history read_history_files choose_videos_from_saved_uploaders edit_sticky_file );
use App::YTDL::Video_Info qw( gather_video_infos );
use App::YTDL::Options    qw( read_config_file set_options get_defaults );

binmode STDIN,  ':encoding(utf-8)';
binmode STDOUT, ':encoding(utf-8)';
binmode STDERR, ':encoding(utf-8)';

my $old_out_codepage;

BEGIN {
    if ( $^O eq "MSWin32" ) {
        require Win32::Console;
        require Win32::Console::ANSI;
        $old_out_codepage = Win32::Console::OutputCP();
        Win32::Console::OutputCP( 65001 );
        print "\e(U";
    }
}

END {
    if ( $^O eq "MSWin32" ) {
        Win32::Console::OutputCP( $old_out_codepage ) if $old_out_codepage;
    }
}


my ( $arg_file, $help );
GetOptions( 'f|file=s@' => \$arg_file, 'h|?|help' => \$help )
or pod2usage( -message => $!, -verbose => 99, -sections => "SYNOPSIS" );


my $my_videos = File::HomeDir->my_videos || curdir; ##
if ( ! -d $my_videos ) {
    say "Could not find the video directory '$my_videos'!";
    exit 1;
}

my $config_home;
if ( which( 'xdg-user-dir' ) ) {
    $config_home = File::HomeDir::FreeDesktop->my_config(); ##
}
else {
    $config_home = File::HomeDir->my_data();
}
my $config_dir = catdir $config_home, 'getvideo';
make_path $config_dir;


my $set = {
    ffmpeg                      => which( 'ffmpeg' ),
    ffprobe                     => which( 'ffprobe' ),

    config_dir                  => $config_dir,
    config_file                 => catfile( $config_dir, 'config.json' ),
    history_file                => catfile( $config_dir, 'uploader_history.json' ),
    sticky_file                 => catfile( $config_dir, 'uploader_sticky.json' ),

    right_margin                => $^O eq 'MSWin32' ? 1 : 2,
    list_sort_order             => 'Desc',
    error_get_download_infos    => [],
};

my $opt = get_defaults();

my $youtube_dl = which( 'youtube-dl' );
if ( ! $youtube_dl ) {
    say "Could not find 'youtube-dl' - 'youtube-dl' is required - http://rg3.github.io/youtube-dl/.";
    exit 1;
};
$set->{youtube_dl} = [ $youtube_dl ];

read_config_file( $opt, $set->{config_file} );
set_options( $set, $opt ) if $help;

push @{$set->{youtube_dl}}, '--config-location', $opt->{yt_dl_config_location}  if $opt->{yt_dl_config_location};
push @{$set->{youtube_dl}}, '--ignore-config',                                  if $opt->{yt_dl_ignore_config};
push @{$set->{youtube_dl}}, '--no-warnings'                                     if $opt->{no_warnings};
push @{$set->{youtube_dl}}, '--user-agent',      $opt->{useragent}              if defined $opt->{useragent};
push @{$set->{youtube_dl}}, '--retries',         $opt->{retries}                if defined $opt->{retries};
push @{$set->{youtube_dl}}, '--socket-timeout',  $opt->{timeout}                if defined $opt->{timeout};
push @{$set->{youtube_dl}}, '--prefer-free-formats'                             if $opt->{prefer_free_formats};


local $| = 1;
print locate( 1, 1 ), cldown;


read_history_files( $set, $opt );


my @ids = @ARGV;

for my $file ( @$arg_file ) {
    open my $fh, '<:encoding(utf-8)', $file or die $!;
    while ( my $line = <$fh> ) {
        next if $line =~ /^\s*\z/;
        next if $line =~ /^\s*#/;
        $line =~ s/^\s+|\s+\z//g;
        push @ids, split /\s+/, $line;
    }
    close $fh or die $!;
}

if ( ! @ids ) {
    my $trs = Term::Form->new();
    my $ids = $trs->readline( 'Enter url/id: ' );
    @ids = split /\s+/, $ids;
    print up( 1 ), cldown;
}

if ( @ids ) {
    my $data = from_arguments_to_choices( $set, $opt, @ids );
    gather_video_infos( $set, $opt, $data );
    download_youtube( $set, $opt, $data, 0 );
    add_uploaders_to_history( $set, $opt, $data ) if $opt->{max_size_history};
}
elsif ( ! @ids && ( %{$set->{history}} || %{$set->{sticky}} ) ) {
    my $old_idx = 0;

    MENU: while ( 1 ) {
        #my @list_size = ( '  ALL', '  50' );
        #my $list_size_regexp = join '|', map { quotemeta } @list_size;
        my ( $uploader, $sticky ) = ( '- Uploader', '  Sticky' );
        my $menu = [ undef, $uploader, $sticky ];
        # Choose
        my $idx = choose(
            $menu,                                  # , $list_size[$set->{small_list_size}
            { prompt => 'Choose:', layout => 3, undef => '  QUIT', clear_screen => 1, index => 1, default => $old_idx }
        );
        if ( ! defined $idx || ! defined $menu->[$idx] ) {
            exit;
        }
        #if ( $opt->{menu_memory} ) {
        #    if ( $old_idx == $idx && ! $ENV{TC_RESET_AUTO_UP} ) {
        #        $old_idx = 0;
        #        next MENU;
        #    }
        #    $old_idx = $idx;
        #}
        if ( $menu->[$idx] eq $uploader ) {
            my @chosen_ids = choose_videos_from_saved_uploaders( $set, $opt );
            next if ! @chosen_ids;
            my $data = from_arguments_to_choices( $set, $opt, @chosen_ids );
            gather_video_infos( $set, $opt, $data );
            download_youtube( $set, $opt, $data, 1 );
        }
        elsif ( $menu->[$idx] eq $sticky ) {
           edit_sticky_file( $set, $opt );
        }
        #if ( $menu->[$idx] =~ /^$list_size_regexp\z/ ) {
        #    $set->{small_list_size}++;
        #    $set->{small_list_size} = 0 if $set->{small_list_size} > $#list_size;
        #    next MENU;
        #}
    }
}


if ( ! @ids ) {
    say "No arguments" and exit;
}


if ( @{$set->{error_get_download_infos}} ) { ##
    say "\nError fetching download infos:";
    for my $video_id ( @{$set->{error_get_download_infos}} ) {
        say '  ' . $video_id;
    }
}



__END__

=pod

=encoding UTF-8

=head1 NAME

getvideo - Download YouTube and other videos.

=head1 VERSION

Version 0.318

=cut

=head1 SYNOPSIS

    getvideo -h|-?|--help

    getvideo

    getvideo url [url ...]

    getvideo -f|--file filename

=head1 DESCRIPTION

Download single videos or choose videos from a playlist or an uploader.

Call C<getvideo> followed by the space separated urls or enter the urls after calling C<getvideo>. If the urls are
entered after calling C<getvideo>, shell metacharacters are escaped automatically.

The urls can also be passed with a file: C<getvideo -f|--file filename>. The urls in the file have to be space separated.

If a passed url results in more than one video, it is shown a menu with the video titles. The user can then choose from
the menu which videos to download. It is possible to filter the video titles of the list menu with a regexp. The filter
can be inverted by adding C<!~> and a space in front of the regexp. Use the C<SpaceBar> to select more than one video -
see L<Term::Choose/USAGE-AND-RETURN-VALUES>.

The different options of C<getvideo> can be reached by calling C<getvideo -h>.

C<App::YTDL> uses L<youtube-dl|http://rg3.github.io/youtube-dl/> to get the info data and to download the videos. To
list the extractors supported by C<youtube-dl> call C<getvideo -h> and select the entry I<List extractors>.

=head1 Options

=head2 HELP

Shows this HELP text.

=head2 INFO

Shows the path and the version of the running C<getvideo>, the path of the video and configuration directories and the
version of C<youtube-dl>. If C<ffmpeg> and C<ffprobe> are available, their version is also shown.

=head2 Directory

=head3 Video directory

Choose an alternative main video directory.

=head3 Extractor directory

Create/use extractor directories.

- no

- yes

=head3 Uploader directory

Create/use uploader directories

- no

- yes

- if chosen from an uploader or a playlist

=head3 Filename format info

Which format info should be added to the filename.

- none (unsafe)

- video-height (unsafe)

- format-id

- format-id and video-height

- format-string

I<none> and I<video-height> are unsafe because the don't result in an unambiguous filename and could therefore overwrite
previously downloaded files.

=head2 Video format

=head3 Resolution

Set the video height.

If set to I<manually>: C<+> => "merge", C<,> => "and", C</> => "or". See C<youtube-dl> documentation format section.

=head3 No video height

Download videos whose height is unknown.

- no

- yes

=head3 Prefer free formats

Prefer free video formats (I<--prefer-free-formats>).

- no

- yes

=head2 Download

=head3 UserAgent

Set the useragent string (I<--user-agent>).

=head3 Download retries

Set the number of download retries (I<--retries>).

=head3 Timeout

Connection timeout in seconds (I<--socket-timeout>).

=head2 History

=head3 Size history

If no arguments are passed to C<getvideo>, the user can choose videos from uploaders saved in the history file and the
sticky file.

I<Size history> sets the limit of the number of uploaders saved in the history file. Setting I<size history> to C<0>
disables the uploader-history.

An uploader can be made sticky. Uploaders made sticky don't count regarding the I<size history> limit. An uploader made
sticky gets also a new timestamp.

When added to the uploader-history an uploader gets the current timestamp. If the I<size history> limit is reached, the
uploader with the oldest timestamp is removed first.

The supported extractors are YouTube and Vimeo.

=head3 History sort

Sort the history

- by name

- by timestamp

=head2 Uploader video list

=head3 List type

- all standard (list all uploader/playlist videos in the menu).

- all fast (list all uploader/playlist videos in the menu).

- latest (list only the latest videos in the menu - Vimeo: 48, YouTube: 50).

If I<all fast> or I<latest fast> is chosen, the download of the required data for the list-menu takes less time since
the data required for the video downloads is fetched later only for the chosen videos.

With I<all fast> or I<latest fast> enabled a progress info is shown.

Supported extractors:

- Vimeo (no view-count available).

- YouTube (the upload-date is relative to now).

=head3 Sort order

Sort the videos in the list-menu by

- upload date (fallback title);

- title

- view count (fallback upload date)

- duration (fallback upload date)

Fallback is used, if the chosen entry is not available.

=head3 Show view count

- if sorted by view count

- always

=head2 Info output

=head3 Disable warnings

Ignore C<youtube-dl> warnings (I<--no-warnings>).

=head3 Max info width

Set the maximum width of the video info output.

=head2 Youtube-dl config file

=head3 youtube-dl config file

Location of the C<youtube-dl> configuration file (I<--config-location>).

=head3 Ignore youtube-dl config file

Do not read C<youtube-dl> configuration files (I<--ignore-config>).

=head2 List extractors

List the extractors supported by C<youtube-dl>.

=head2 Extractor descriptions

Output descriptions of supported extractors.

=head1 REQUIREMENTS

=head2 Perl version

Requires Perl version 5.10.0 or greater.

=head2 youtube-dl

A recent version of L<youtube-dl|http://rg3.github.io/youtube-dl/> is required.

=head2 ffmpeg and ffprobe

The I<merge> feature requires C<ffmpeg> and C<ffprobe>.

=head2 Monospaced font

It is required a terminal that uses a monospaced font which supports the printed characters.

=head1 CREDITS

C<App::YTDL> uses L<youtube-dl|http://rg3.github.io/youtube-dl/> to get the data required for the video download.

Thanks to the L<Perl-Community.de|http://www.perl-community.de> and the people form
L<stackoverflow|http://stackoverflow.com> for the help.

=head1 AUTHOR

Kuerbis <cuer2s@gmail.com>

=head1 LICENSE AND COPYRIGHT

Copyright (C) 2013-2020 Kuerbis.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For
details, see the full text of the licenses in the file LICENSE.

=cut