#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

################### Localfiles class #################
package Programme::localfiles;

use Env qw[@PATH];
#use MP3::Info; # better eval this module
use Fcntl;
use HTML::Entities;
use strict;
use URI;
use Cwd 'abs_path';
use File::Basename;

# Inherit from Programme class
use base 'Programme';

# Class vars
# Global options
my $opt;


# Constructor
# Usage: $prog{$pid} = Programme->new( 'pid' => $pid, 'name' => $name, <and so on> );
sub new {
	my $type = shift;
	my %params = @_;
	my $self = {};
	for (keys %params) {
		$self->{$_} = $params{$_};
	}
	# Ensure the subclass $opt var is pointing to the Superclass global optref
	$opt = $Programme::optref;
	bless $self, $type;
}


sub index_min { return 900001 }
sub index_max { return 999999 }


# Class cmdline Options
sub opt_format {
	return {
		localfilesdirs		=> [ 0, "localfilesdirs=s", 'Config', '--localfilesdirs <dir>[,dir,]', "Directories/Folders to scan for new files"],
		outputlocalfiles	=> [ 1, "outputlocalfiles=s", 'Output', '--outputlocalfiles <dir>', "Output directory for localfiles recordings"],
	};
}


# Method to return optional list_entry format
sub optional_list_entry_format {
	my $prog = shift;
	my @format;
	for ( qw/ channel categories / ) {
		push @format, $prog->{$_} if defined $prog->{$_};
	}
	return ', '.join ', ', @format;
}


# cache expiry in seconds
sub expiry {
	return ( 999999999999 ); # forever
}


# Returns the modes to try for this prog type
sub modelist {
	return 'localfiles';
}



# Recursive function to recursively find files in a directory
sub find_matching_files {
	my $dir = shift;
	my $file_regex = shift || '.+';
	$dir =~ s|\/+$||g;
	my @filelist;
	my ( $node, $lvl_counter, $list_length );
	if ( opendir( DIR, $dir ) ) {
		my @lines = grep !/^(\.\.|\.)\/?$/, readdir( DIR );
		closedir( DIR );
		for ( @lines ) {
			$node = $dir.'/'.$_;
			if( -d $node && ! -l $node ) {
				push @filelist, find_matching_files( $node, $file_regex );
				#main::logger "DEBUG: dir = $node\n";
			} elsif ( $node =~ m{$file_regex}i ) {
				push @filelist, $node;
				#main::logger "DEBUG: file = $node\n";
			}
		}
	}
	return @filelist;
}



# Usage: Programme::localfiles->get_links( $progref, 'localfiles' );
sub get_links {
	shift; # ignore obj ref
	my $progref = shift;
	my $prog_type = shift;

	# Check if we have MP3::Info
	eval "use MP3::Info";
	if ($@) {
		main::logger "WARNING: Please install the MP3::Info perl module to use '$prog_type' plugin\n";
		#main::logger "WARNING: Please download and run latest installer or install the MP3::Info perl module to use '$prog_type' plugin\n";
		return 0;
	};

	# error if no dirs specified
	if ( ! $opt->{localfilesdirs} ) {
		main::logger "ERROR: Please set --localfilesdirs to point to your collection if using the '$prog_type' plugin\n";
		return 1;
	}

	main::logger "INFO: Getting Local Files Index from $opt->{localfilesdirs}\n";

	# Get mp3 file list
	my @files;
	my $ext = 'mp3';
	for my $scandir ( split /,/, $opt->{localfilesdirs} ) {
		main::logger "INFO: Scanning $scandir\n";
		my @scan = find_matching_files( $scandir, '.+\.'.$ext.'$' );
		main::logger "INFO: Got ".($#scan+1)." $ext files\n";
		push @files, @scan;
	}

	for my $file ( @files ) {
		chomp( $file );

		my $pid = abs_path( $file );

		# Read MP3 tags into a hash for file
		my $info = get_mp3info( $file );
		my $tag = get_mp3tag( $file, undef, undef, 1 );

		# dump tags
		#main::logger "TAG: $_=$tag->{$_}\n" for keys %{ $tag };

		# Clean data - removes unicode :-(
		#StringUtils::clean_utf8_and_whitespace( $tag->{$_} ) for keys %{ $tag };

		# TAGS: YEAR,RIPPING TOOL,ARTIST,COMMENT,TITLE,ALBUM,RELEASE TYPE,GENRE,RIP DATE,TRACKNUM,TAGVERSION,SOURCE
		# INFO: SIZE,OFFSET,MS,STEREO,SECS,PADDING,LAME,MM,COPYRIGHT,SS,LAYER,MODE,FREQUENCY,VBR,TIME,FRAMES,BITRATE,VBR_SCALE,VERSION,FRAME_LENGTH

		# Skip if there is no track name
		if ( ! ( $tag->{'TITLE'} && $tag->{'ALBUM'} && $tag->{'ARTIST'} ) ) {
			## Maybe guess the metadata from the filename here?
			#main::logger "WARNING: $pid has no title - skipping\n" if $opt->{verbose};
			#next;
			main::logger "WARNING: $pid has no title - guessing\n" if $opt->{verbose};
			if ( basename( $file ) =~ m{^(\d*?)[\w\-]*?(.+?)\.(\w+)$} ) {
				#$tag->{'TITLE'} = "$tag->{'TRACKNUM'}_$tag->{'ALBUM'}" if $tag->{'TRACKNUM'} && $tag->{'ALBUM'} && ! $tag->{'TITLE'};
				$tag->{'TITLE'} = $tag->{'TITLE'} || $2;
				$tag->{'ALBUM'} = $tag->{'ALBUM'} || $2;
				$tag->{'ARTIST'} = $tag->{'ARTIST'} || $2;
			}
		}
		main::logger '.';

		my @category;
		push @category, $tag->{'GENRE'};

		my @description;
		for ( "($info->{'BITRATE'}kbps/$info->{'FREQUENCY'}kHz)", $tag->{'COMMENT'}, $tag->{'RIPPING TOOL'} ) {
			push @description, $_ if $_;
		}

		# Get thumbnail from header
		my $thumbnail;
		my $dir = dirname( $file );
		# better to use opendir and regex search filename
		for ( qw/ front.jpg cover.jpg AlbumArtSmall.jpg / ) {
			$thumbnail = "file://${dir}/${_}" if -f "${dir}/${_}";
		}

		# Skip if this pid is a duplicate
		if ( defined $progref->{$pid} ) {
			main::logger "WARNING: '$pid, $progref->{$pid}->{name} - $progref->{$pid}->{episode}, $progref->{$pid}->{channel}' already exists (this channel = $_)\n" if $opt->{verbose};
			next;
		}

		# build data structure
		$progref->{$pid} = Programme::localfiles->new(
			'pid'		=> $pid,
			'name'		=> $tag->{'ALBUM'},
			'versions'	=> 'default',
			'episode'	=> $tag->{'TITLE'},
			'desc'		=> join(', ', @description),
			'available'	=> $tag->{'YEAR'} || $tag->{'RIP DATE'},
			'duration'	=> $info->{'SECS'},
			'thumbnail'	=> $thumbnail,
			'channel'	=> $tag->{'ARTIST'},
			'categories'	=> join(',', @category),
			'type'		=> $prog_type,
			'web'		=> "file://${file}",
		);

		# Sanitize element values
		for ( values %{ $progref->{$pid} } ) {
			s/[\n\r]/ /g;
			s/(^[\s_,]+|[\s_,]+$)//g;
		}
	}

	main::logger "\n";
	return 0;
}



# Gets media streams data for this version pid
# $media = file|undef
sub get_stream_data {
	my ( $prog, $verpid, $media ) = @_;
	my $data = {};

	$opt->{quiet} = 0 if $opt->{streaminfo};

	$data->{localfiles}->{ext} = $prog->{web};
	$data->{localfiles}->{ext} =~ s|^.*\.(\w+)$|$1|g;
	$data->{localfiles}->{streamer} = 'filestreamonly';
	$data->{localfiles}->{streamurl} = $prog->{web};
	$data->{localfiles}->{type} = 'Local File stream';

	# Return a hash with media => url if '' is specified - otherwise just the specified url
	if ( ! $media ) {
		return $data;
	} else {
		# Make sure this hash exists before we pass it back...
		$data->{$media}->{exists} = 0 if not defined $data->{$media};
		return $data->{$media};
	}
}



sub download {
	my ( $prog, $ua, $mode, $version, $version_pid ) = ( @_ );

	# if subsonly required then skip
	return 'skip' if $opt->{subsonly};

	# Determine the correct filename and extension for this download
	my $filename_orig = $prog->{pid};
	$filename_orig =~ s|^.+/(.+?)\.\w+$|$1|g;
	$prog->{ext} = $prog->{streams}->{$version}->{$mode}->{ext};

	# Determine the correct filenames for this download
	return 'skip' if $prog->generate_filenames( $ua, "<longname> - <episode> $filename_orig" );

	# Create dir for prog if not streaming-only
	if ( ( ! ( $opt->{stdout} && $opt->{nowrite} ) ) && ( ! $opt->{test} ) ) {
		$prog->create_dir();
	}

	# Skip from here if we are only testing downloads
	return 1 if $opt->{test};
	# Instantiate new streamer based on streamdata
	my $class = "Streamer::$prog->{streams}->{$version}->{$mode}->{streamer}";
	my $stream = $class->new;
	return $stream->get( $ua, $prog->{pid}, $prog );
}

1;
